What
core.fails_gate(results, fail_on) looks up the floor with SEVERITY_ORDER.get(fail_on, 99). An unrecognized severity string maps to rank 99 (below every real severity), so the comparison SEVERITY_ORDER.get(m.severity, 99) <= 99 is true for every match. A typo'd or unknown floor silently becomes the most permissive gate rather than a no-op or an error.
Reproduce
from c2detect.core import scan_observations, fails_gate
res = scan_observations([{"jarm": "07d14d16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1"}])
print(fails_gate(res, "criticl")) # typo -> True (gate fails on any match)
print(fails_gate(res, "nonsense")) # -> True
Impact
Low in practice for the CLI: argparse restricts --fail-on to valid choices, so the CLI never reaches this. But fails_gate / fails_gate_with_ai are part of the public API (exported from c2detect), and a library caller passing an unvalidated severity gets a gate that fails on everything — the opposite of a safe default.
Suggested fix
Treat an unknown floor as "no gate" (return False) or raise ValueError, rather than defaulting to rank 99. If a real floor genuinely means "everything", callers can pass "info" explicitly. A one-line guard in fails_gate:
if fail_on not in SEVERITY_ORDER:
return False # or: raise ValueError(f"unknown severity floor: {fail_on!r}")
Same guard applies to the AI-aware fails_gate_with_ai and the correlate-gate path in the CLI.
What
core.fails_gate(results, fail_on)looks up the floor withSEVERITY_ORDER.get(fail_on, 99). An unrecognized severity string maps to rank99(below every real severity), so the comparisonSEVERITY_ORDER.get(m.severity, 99) <= 99is true for every match. A typo'd or unknown floor silently becomes the most permissive gate rather than a no-op or an error.Reproduce
Impact
Low in practice for the CLI:
argparserestricts--fail-onto valid choices, so the CLI never reaches this. Butfails_gate/fails_gate_with_aiare part of the public API (exported fromc2detect), and a library caller passing an unvalidated severity gets a gate that fails on everything — the opposite of a safe default.Suggested fix
Treat an unknown floor as "no gate" (return
False) or raiseValueError, rather than defaulting to rank 99. If a real floor genuinely means "everything", callers can pass"info"explicitly. A one-line guard infails_gate:Same guard applies to the AI-aware
fails_gate_with_aiand the correlate-gate path in the CLI.