feat(controls): B1 — custom semgrep detectors for 4 CRA controls (#218)
CI / Check (push) Skipped
CI / Detect Changes (push) Successful in 3s
CI / Deploy Dashboard (push) Skipped
CI / Deploy Docs (push) Skipped
CI / Deploy MCP (push) Skipped
CI / Deploy Agent (push) Failing after 4s

This commit was merged in pull request #218.
This commit is contained in:
2026-07-21 12:31:20 +00:00
parent 36a49cdeac
commit 38fedc661b
5 changed files with 338 additions and 44 deletions
+69 -3
View File
@@ -66,6 +66,14 @@ pub struct ControlMap {
const CRA_MAP_JSON: &str = include_str!("../data/cra_control_map.json");
/// Whether an authored rule id `bound` matches a scanner's emitted rule id
/// `actual`. semgrep prefixes local-rule check_ids with a path
/// (`tmp.compliance-cra-semgrep.cra-ai-1-flask-debug-enabled`), so match the final
/// id segment rather than requiring exact equality.
fn rule_id_matches(bound: &str, actual: &str) -> bool {
actual == bound || actual.ends_with(&format!(".{bound}"))
}
impl ControlMap {
/// Load the built-in CRA control map (the embedded, authored LUT).
pub fn cra() -> Result<Self, MapError> {
@@ -80,12 +88,28 @@ impl ControlMap {
/// Controls whose bindings include the given `tool` + `cwe` — used to attach a
/// raw tool finding back to the control(s) it's evidence for.
pub fn controls_for(&self, tool: &str, cwe: &str) -> Vec<&ControlEntry> {
self.controls_for_finding(tool, Some(cwe), None)
}
/// Controls a tool finding is evidence for, matched by CWE and/or the specific
/// rule id that fired. Off-the-shelf findings bind by CWE; our custom detectors
/// bind by rule id (precise — a broad CWE would over-attribute and then the
/// grounded judge could drop a genuine finding as a control false positive).
pub fn controls_for_finding(
&self,
tool: &str,
cwe: Option<&str>,
rule_id: Option<&str>,
) -> Vec<&ControlEntry> {
self.controls
.iter()
.filter(|c| {
c.scans
.iter()
.any(|s| s.tool == tool && s.cwe.iter().any(|w| w == cwe))
c.scans.iter().any(|s| {
s.tool == tool
&& (cwe.is_some_and(|w| s.cwe.iter().any(|x| x == w))
|| rule_id
.is_some_and(|r| s.rules.iter().any(|b| rule_id_matches(b, r))))
})
})
.collect()
}
@@ -160,4 +184,46 @@ mod tests {
assert!(s.needs_tooling > 0);
assert!(s.not_code_checkable > 0);
}
#[test]
fn rule_id_matching_handles_semgrep_path_prefix() {
let bound = "cra-ai-1-flask-debug-enabled";
assert!(rule_id_matches(bound, bound)); // exact
assert!(rule_id_matches(
bound,
"tmp.compliance-cra-semgrep.cra-ai-1-flask-debug-enabled"
)); // semgrep path prefix
assert!(!rule_id_matches(
bound,
"cra-ai-1-flask-debug-enabled-extra"
)); // not a suffix segment
assert!(!rule_id_matches(
bound,
"python.lang.security.exec-detected"
)); // unrelated
}
#[test]
fn custom_rule_finding_attaches_to_control_by_rule_id() {
let map = ControlMap::cra().unwrap();
// cra-ai-1 is now tool-covered by custom rules.
assert_eq!(map.coverage("cra-ai-1").unwrap().status, Coverage::Covered);
// A prefixed check_id still maps back to cra-ai-1 by rule id.
let hits =
map.controls_for_finding("semgrep", None, Some("tmp.x.cra-ai-1-tls-verify-disabled"));
assert!(hits.iter().any(|c| c.control == "cra-ai-1"));
}
#[test]
fn custom_rule_controls_do_not_bind_by_broad_cwe() {
let map = ControlMap::cra().unwrap();
// cra-ai-1 rules emit CWE-489 in metadata, but the LUT binds by rule id
// only (cwe: []) — so a stray CWE-489 finding must NOT attach to it.
assert!(map.controls_for("semgrep", "CWE-489").is_empty());
// The CWE path for off-the-shelf findings is unchanged.
assert!(map
.controls_for("semgrep", "CWE-798")
.iter()
.any(|c| c.control == "cra-ai-8"));
}
}