fn override_matches(override_rule: &RuleOverride, context: &RuleMatchContext<'_>) -> bool {
if override_rule
.signal
.as_deref()
.is_some_and(|signal| Some(signal) != context.signal)
{
return false;
}
if override_rule
.language
.as_deref()
.is_some_and(|language| !context.languages.contains(&language))
{
return false;
}
if override_rule
.framework
.as_deref()
.is_some_and(|framework| !context.frameworks.contains(&framework))
{
return false;
}
if override_rule
.runtime
.as_deref()
.is_some_and(|runtime| !context.runtimes.contains(&runtime))
{
return false;
}
if override_rule
.paradigm
.as_deref()
.is_some_and(|paradigm| !context.paradigms.contains(¶digm))
{
return false;
}
if override_rule
.role
.as_deref()
.is_some_and(|role| !context.roles.contains(&role))
{
return false;
}
true
}
fn is_test_override(override_rule: &RuleOverride) -> bool {
matches!(override_rule.role.as_deref(), Some("test" | "rust-test"))
}
fn apply_override(
override_rule: &RuleOverride,
current: Severity,
fallback_risk: Option<crate::knowledge::model::RuleRiskAdjustment>,
) -> RuleDecision {
let risk_signal = override_rule.risk.clone().or(fallback_risk);
match override_rule.action {
RuleDecisionAction::Apply => RuleDecision {
action: RuleDecisionAction::Apply,
severity: override_rule.severity.unwrap_or(current),
reason: override_rule.reason.clone(),
risk_signal,
},
RuleDecisionAction::Suppress => RuleDecision {
action: RuleDecisionAction::Suppress,
severity: Severity::Info,
reason: override_rule.reason.clone(),
risk_signal: None,
},
RuleDecisionAction::Downgrade => {
let severity = override_rule
.severity
.filter(|severity| *severity < current)
.unwrap_or(current);
RuleDecision {
action: RuleDecisionAction::Downgrade,
severity,
reason: override_rule.reason.clone(),
risk_signal,
}
}
RuleDecisionAction::Upgrade => {
let severity = override_rule
.severity
.filter(|severity| *severity > current)
.unwrap_or(current);
RuleDecision {
action: RuleDecisionAction::Upgrade,
severity,
reason: override_rule.reason.clone(),
risk_signal,
}
}
}
}
fn language_support_satisfies(
rule: &crate::knowledge::model::RuleApplicability,
context_languages: &[&str],
minimum_support: crate::knowledge::model::SupportLevel,
) -> bool {
context_languages.iter().any(|language| {
(rule.languages.is_empty() || rule.languages.contains(*language))
&& profile_by_id(language).is_some_and(|profile| profile.support >= minimum_support)
})
}
fn language_ids_for_file(file: &FileFacts, context: &AuditContext) -> Vec<&'static str> {
let mut languages = Vec::new();
if let Some(language) = file
.language
.as_deref()
.and_then(language_id_for_name)
.filter(|language| *language != "unknown")
{
languages.push(language);
}
let context_language = context.language_id();
if context_language != "unknown" {
languages.push(context_language);
}
languages
}
fn language_ids_for_project(facts: &ScanFacts) -> Vec<&'static str> {
let mut languages = facts
.languages
.iter()
.filter_map(|language| language_id_for_name(&language.name))
.collect::<Vec<_>>();
if languages.is_empty() {
languages.extend(
facts
.files
.iter()
.filter_map(|file| file.language.as_deref())
.filter_map(language_id_for_name),
);
}
languages
}
fn framework_ids_for_project(facts: &ScanFacts) -> Vec<&'static str> {
let mut frameworks = facts
.detected_frameworks
.iter()
.filter_map(framework_id)
.collect::<Vec<_>>();
for project in &facts.framework_projects {
frameworks.extend(project.frameworks.iter().filter_map(framework_id));
}
dedup_static_ids(&mut frameworks);
frameworks
}
fn framework_id(framework: &DetectedFramework) -> Option<&'static str> {
match framework {
DetectedFramework::ReactNative { .. } => Some("react-native"),
DetectedFramework::Expo { .. } => Some("expo"),
DetectedFramework::NextJs { .. } => Some("nextjs"),
DetectedFramework::React { .. } => Some("react"),
DetectedFramework::Vue { .. } => Some("vue"),
DetectedFramework::Angular { .. } => Some("angular"),
DetectedFramework::Svelte { .. } => Some("svelte"),
DetectedFramework::NestJs { .. } => Some("nestjs"),
DetectedFramework::Express { .. } => Some("express"),
DetectedFramework::Django { .. } => Some("django"),
DetectedFramework::Flask { .. } => Some("flask"),
DetectedFramework::FastApi { .. } => Some("fastapi"),
DetectedFramework::Gin { .. } => Some("gin"),
DetectedFramework::Echo { .. } => Some("echo"),
DetectedFramework::Fiber { .. } => Some("fiber"),
}
}
fn dedup_static_ids(values: &mut Vec<&'static str>) {
let mut seen = HashSet::new();
values.retain(|value| seen.insert(*value));
}