From 8ffe74a13f9d855ae70d33babe0934e1609a5e82 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Jul 2026 09:28:11 +0000 Subject: [PATCH] fix(monitor): extend high-FP set after auditing the residual verdicts (#30) Completed the residual single-rule MALICIOUS audit. Ran each remaining rule against benign inputs; six more over-flag legitimate content and now get the verify-or-downgrade treatment: - MALWAR-CMD-002 3 legit 'npx -y @mcp/...' setup lines -> MALICIOUS - MALWAR-CRED-002 onboarding prose ('enter your API key', 'paste your token') - MALWAR-EXFIL-001 skill-tooling reads of ~/.claude/ and .cursor/ - MALWAR-EXFIL-003 'curl -X POST -d "$(cat file)"' (normal API upload) - MALWAR-PI-001 security/detection skills that quote injection phrases - MALWAR-HIJACK-001 roleplay/persona skills ('you are now...', 'your new role is') Held up and stay confident on a single hit (no FP in testing): PERSIST-001 (cron/systemd/.bashrc), EXFIL-002 (crypto-wallet), FRAUD-002 (front-running). Recall preserved: all still fire on the malicious fixtures; corroborated (2+ rule) verdicts unaffected. On the current snapshot this leaves only 20 unverified single-rule MALICIOUS (PERSIST-001 x15, EXFIL-002 x4, FRAUD-002 x1), all on rules that did not over-flag. Full unit suite (1445) passes. Co-Authored-By: Claude --- src/malwar/monitor/escalation.py | 47 ++++++++++++++++++++------------ tests/unit/test_escalation.py | 38 ++++++++++++++++---------- 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/malwar/monitor/escalation.py b/src/malwar/monitor/escalation.py index 37ae24c..bb4ff0e 100644 --- a/src/malwar/monitor/escalation.py +++ b/src/malwar/monitor/escalation.py @@ -40,29 +40,42 @@ # MALICIOUS threshold in ScanResult.verdict.) _MALICIOUS_RISK = 75 -# 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-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 +# Rules that, on a single line match, are known to over-flag legitimate content. +# Each was confirmed by running the rule against real benign inputs (see the +# residual-verdict audit in issue #30): +# * MALWAR-CMD-001 ``curl ... | sh`` -- the documented install path for a +# large share of legitimate dev tools (installer-host allowlisting handles +# dedicated-domain cases; multi-tenant hosts remain). +# * MALWAR-ENV-001 ordinary prose ("the key you need to set", "run env"). +# * MALWAR-PERSIST-002 self-referential SKILL.md/CLAUDE.md/.claude file ops, +# including reads ("cat SKILL.md | grep") and legit skill-authoring tools. +# * MALWAR-MULTI-001 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. 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. +# * MALWAR-CMD-002 ``npx -y `` -- the standard way to run MCP servers +# and CLI tools; a few in one skill push it over the MALICIOUS line. +# * MALWAR-CRED-002 onboarding prose ("enter your API key", "paste your token"). +# * MALWAR-EXFIL-001 reads of ~/.claude/ and .cursor/ that skill-tooling does. +# * MALWAR-EXFIL-003 ``curl -X POST -d "$(cat file)"`` -- a normal API upload. +# * MALWAR-PI-001 security/detection skills that quote injection phrases. +# * MALWAR-HIJACK-001 roleplay/persona skills ("you are now...", "your new role is"). +# A MALICIOUS verdict resting on a *single* one of these rules is fragile: one +# regex, no corroboration, no semantic read. Such verdicts are re-checked +# (escalated) and, if not authoritatively confirmed, downgraded to SUSPICIOUS +# rather than published as a conviction. Tighter, low-FP rules are deliberately +# NOT listed and stay confident on a single hit: e.g. MALWAR-PERSIST-001 +# (cron/systemd/.bashrc), MALWAR-EXFIL-002 (crypto-wallet access) and +# MALWAR-FRAUD-002 (agentic front-running). HIGH_FP_RULES: frozenset[str] = frozenset({ "MALWAR-CMD-001", "MALWAR-ENV-001", "MALWAR-PERSIST-002", "MALWAR-MULTI-001", + "MALWAR-CMD-002", + "MALWAR-CRED-002", + "MALWAR-EXFIL-001", + "MALWAR-EXFIL-003", + "MALWAR-PI-001", + "MALWAR-HIJACK-001", }) diff --git a/tests/unit/test_escalation.py b/tests/unit/test_escalation.py index b8e63e1..b8c13d6 100644 --- a/tests/unit/test_escalation.py +++ b/tests/unit/test_escalation.py @@ -29,25 +29,35 @@ def mrec(rules: list[str], *, verdict: str = "MALICIOUS", risk: int = 92): 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"])) + # Every rule confirmed to over-flag legitimate content in the issue #30 + # residual-verdict audit is treated as fragile on a single-rule hit. + for rid in ( + "MALWAR-CMD-001", + "MALWAR-ENV-001", + "MALWAR-PERSIST-002", + "MALWAR-MULTI-001", + "MALWAR-CMD-002", + "MALWAR-CRED-002", + "MALWAR-EXFIL-001", + "MALWAR-EXFIL-003", + "MALWAR-PI-001", + "MALWAR-HIJACK-001", + ): + assert is_fragile_malicious(mrec([rid])), rid + + def test_tight_rules_stay_confident(self): + # These did not over-flag in testing; a single hit remains a confident + # verdict, not fragile. + for rid in ("MALWAR-PERSIST-001", "MALWAR-EXFIL-002", "MALWAR-FRAUD-002"): + assert not is_fragile_malicious(mrec([rid])), rid def test_corroborated_two_rules_not_fragile(self): assert not is_fragile_malicious(mrec(["MALWAR-CMD-001", "MALWAR-PERSIST-002"])) def test_single_low_fp_rule_not_fragile(self): - # A single non-high-FP rule (e.g. exfiltration) is evidence-based; keep it. - assert not is_fragile_malicious(mrec(["MALWAR-EXFIL-001"])) + # A single low-FP rule (e.g. crypto-wallet exfiltration) is evidence-based; + # keep it a confident verdict. + assert not is_fragile_malicious(mrec(["MALWAR-EXFIL-002"])) def test_non_malicious_verdict_not_fragile(self): assert not is_fragile_malicious(mrec(["MALWAR-CMD-001"], verdict="SUSPICIOUS", risk=40))