use super::{ArtifactTaintRule, ArtifactTaintRuleGroup, TaintSinkKind, TaintSourceKind};
use std::collections::BTreeMap;
pub(super) fn default_rules() -> Vec<ArtifactTaintRule> {
const YAML: &str = include_str!("../taint_rules.yaml");
serde_yaml::from_str(YAML)
.unwrap_or_else(|err| panic!("built-in taint_rules.yaml must parse: {err}"))
}
pub(super) fn group_rules(rules: Vec<ArtifactTaintRule>) -> Vec<ArtifactTaintRuleGroup> {
let mut groups: BTreeMap<(TaintSourceKind, TaintSinkKind), Vec<ArtifactTaintRule>> =
BTreeMap::new();
for rule in rules {
groups
.entry((rule.source, rule.sink))
.or_default()
.push(rule);
}
let mut result: Vec<_> = groups
.into_iter()
.map(|((source, sink), rules)| ArtifactTaintRuleGroup {
source,
sink,
rules,
})
.collect();
result.sort_by(|a, b| {
let max_sev = |group: &ArtifactTaintRuleGroup| group.rules.iter().map(|r| r.severity).max();
max_sev(b).cmp(&max_sev(a))
});
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_rules_yaml_parses_cleanly() {
let rules = default_rules();
assert!(
!rules.is_empty(),
"built-in taint_rules.yaml must contribute at least one rule",
);
}
#[test]
fn group_rules_preserves_every_default_rule() {
let rules = default_rules();
let original_count = rules.len();
let groups = group_rules(rules);
let regrouped_count: usize = groups.iter().map(|g| g.rules.len()).sum();
assert_eq!(
regrouped_count,
original_count,
"group_rules must preserve every input rule (lost {} rules)",
original_count - regrouped_count,
);
}
}