Covers the pattern-expressible slice of the needs_tooling bucket that no
off-the-shelf ruleset digs out, keeping detection deterministic (LLM only
FP-filters downstream, never detects):
- cra-ai-1 Secure-by-Default: flask/django debug, TLS verify=False, CORS '*'
- cra-ai-7 Strong auth: password/secret hashed with md5/sha1 (metavar-gated)
- cra-ai-10 Session mgmt: Secure/HttpOnly = false cookies (py + express)
- cra-ai-14 Data at rest: ECB/DES/3DES + node createCipher
Rules ship in the binary (include_str!) and stage to a temp file at scan time,
added as a second --config alongside --config=auto (no deploy/volume change).
Wiring: control-map gains controls_for_finding (match by CWE and/or rule id);
custom controls bind by rule id with cwe:[] so a broad CWE can't over-attribute
and let the judge FP-drop a genuine finding. rule_id_matches tolerates semgrep's
path prefix on local check_ids. Triage now maps by rule id too (a custom finding
carries no LUT CWE). LUT: cra-ai-1,7,10,14 needs_tooling->covered (covered 9->13).
Validated: all 9 rules fire on positive fixtures, 0 on clean. ControlCheckSpec
gains Serialize/Deserialize (unrelated-safe; already used by the index cache).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>