diff --git a/src/malwar/monitor/escalation.py b/src/malwar/monitor/escalation.py index f6ff583..37ae24c 100644 --- a/src/malwar/monitor/escalation.py +++ b/src/malwar/monitor/escalation.py @@ -40,17 +40,30 @@ # MALICIOUS threshold in ScanResult.verdict.) _MALICIOUS_RISK = 75 -# Rules that, on a single line match, are known to over-flag legitimate content: +# Rules that, on a single line match, are known to over-flag legitimate content +# (each confirmed by running the rule against real benign inputs): # * MALWAR-CMD-001 fires on any ``curl ... | sh`` -- the documented install path # for a large share of legitimate dev tools (installer-host allowlisting in # the rule handles the dedicated-domain cases; multi-tenant hosts remain). -# * MALWAR-ENV-001's broad pattern matches ordinary prose ("…the key you need -# to set", "…by running env"). +# * MALWAR-ENV-001's broad pattern matches ordinary prose ("the key you need +# to set", "by running env"). +# * MALWAR-PERSIST-002 matches self-referential file ops on SKILL.md/CLAUDE.md/ +# .claude/, including *reads* ("cat SKILL.md | grep") and legitimate +# skill-authoring tools that write a CLAUDE.md. +# * MALWAR-MULTI-001 matches benign prose about deferred/quiet execution +# ("applies the patch without showing the full diff"). # A MALICIOUS verdict resting on a *single* one of these rules is fragile: it # has never been corroborated by a second rule or a semantic pass. Such verdicts # are re-checked (escalated) and, if not authoritatively confirmed, downgraded -# to SUSPICIOUS rather than published as a confident conviction. -HIGH_FP_RULES: frozenset[str] = frozenset({"MALWAR-CMD-001", "MALWAR-ENV-001"}) +# to SUSPICIOUS rather than published as a confident conviction. Tighter, +# lower-FP rules (e.g. MALWAR-PERSIST-001 for cron/systemd/.bashrc persistence) +# are deliberately NOT listed: a single hit from them stays a confident verdict. +HIGH_FP_RULES: frozenset[str] = frozenset({ + "MALWAR-CMD-001", + "MALWAR-ENV-001", + "MALWAR-PERSIST-002", + "MALWAR-MULTI-001", +}) def is_fragile_malicious(record: SkillRecord) -> bool: diff --git a/tests/unit/test_escalation.py b/tests/unit/test_escalation.py index 54aecf7..b8e63e1 100644 --- a/tests/unit/test_escalation.py +++ b/tests/unit/test_escalation.py @@ -31,6 +31,16 @@ class TestFragileMalicious: def test_single_high_fp_rule_is_fragile(self): assert is_fragile_malicious(mrec(["MALWAR-CMD-001"])) assert is_fragile_malicious(mrec(["MALWAR-ENV-001"])) + # Added after auditing the residual single-rule MALICIOUS tier: these + # two over-flag legitimate content (self-referential SKILL.md file ops; + # benign "without showing output" prose). + assert is_fragile_malicious(mrec(["MALWAR-PERSIST-002"])) + assert is_fragile_malicious(mrec(["MALWAR-MULTI-001"])) + + def test_tight_persistence_rule_stays_confident(self): + # MALWAR-PERSIST-001 (cron/systemd/.bashrc) did not over-flag in testing; + # a single hit remains a confident verdict, not fragile. + assert not is_fragile_malicious(mrec(["MALWAR-PERSIST-001"])) def test_corroborated_two_rules_not_fragile(self): assert not is_fragile_malicious(mrec(["MALWAR-CMD-001", "MALWAR-PERSIST-002"]))