refactor(llm-security): build the SIG ruleset from vendored commons (malware-signatures 0.1.0)

Fifth and last consumer swap of v8 Phase 5 step 4. The seven known-bad-identity
signatures stop living in knowledge/signatures.json and are built from the
vendored commons artifact signatures/malware-signatures.json instead.

Measured before the swap over all seven positions -- id, family, severity,
pattern, description, provenance, key order, and recompilation identity under
the engine's unconditional `i` flag: zero divergences over 56 checks, in order.
The commons copy was extracted from this repository's own file at b0de0ca and
had not drifted.

knowledge/signatures.json is REMOVED rather than left in place. Keeping it would
have left two files spelling one table with nothing gating the drift, and its
golden `file:` pin would have gone on passing while pinning bytes no scanner
reads -- a gate reporting success without running. The pin is replaced by a
walked-module anchor over SIGNATURE_RULES, which is strictly stronger: the pin
covered the bytes on disk, the walk covers what `new RegExp` made of them.
Golden diff was exactly that and nothing else: 7 ADDED, 1 REMOVED, 0 CHANGED
(102/7/5 -> 109/7/4), each added source verified equal to the recompiled commons
pattern.

compileRules() moves into the new lib module and is exported, so the built-in
ruleset and the operator's sig.custom_rules_path path keep one implementation
rather than two copies of the defaulting logic.

Coverage by construction, not by memory: the probe table in the scanner test is
asserted against the LOADED ruleset, so a rule commons adds cannot arrive
without an end-to-end probe. Mutation of the vendored JSON fires in three
directions -- under-match (xmrig alternative dropped) reddens two scanner tests
plus golden; over-match (webshell rule widened to a bare `shell`) reddens the
clean-fixture false-positive probe plus golden; reorder reddens the declared-
order test plus golden.

Loud failure is contract: an unresolvable commons writes one line to stderr
rather than silently disabling known-malware detection, and never throws.

Suite 2247 / 2241 pass / 6 skipped / 0 fail. suite-counts.json untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0151x4FVg9Mn55C2LvHLpHKo
This commit is contained in:
Kjell Tore Guttormsen 2026-08-13 21:28:14 +02:00
commit bbada84e9f
12 changed files with 480 additions and 132 deletions

View file

@ -0,0 +1,42 @@
{
"$comment": "Fixture for malware-signatures.test.mjs graceful-degradation cases. Not a commons artifact: a hand-written ruleset whose rules are individually broken in the ways vendored data can plausibly be broken, plus one rule carrying only the two required fields so the loader's documented defaults (family -> 'unknown', severity -> 'high', description -> the id, provenance -> null) can be observed rather than assumed. A builder that survives this file survives a corrupt commons without throwing at module load.",
"version": "0.0.0-fixture",
"id": "fixture/malformed-malware-signatures",
"rules": [
{
"id": "FIX-OK-001",
"family": "webshell",
"severity": "critical",
"pattern": "fixture-shape-a",
"description": "a well-formed rule carrying all six fields",
"provenance": "fixture"
},
{
"id": "FIX-UNCOMPILABLE-001",
"family": "webshell",
"severity": "critical",
"pattern": "(?:unclosed[",
"description": "uncompilable: unbalanced group and class",
"provenance": "fixture"
},
{
"family": "webshell",
"severity": "high",
"pattern": "fixture-missing-id",
"description": "no id — dropped by the loader",
"provenance": "fixture"
},
{
"id": "FIX-NO-PATTERN-001",
"family": "webshell",
"severity": "high",
"description": "no pattern — dropped by the loader",
"provenance": "fixture"
},
null,
{
"id": "FIX-DEFAULTS-001",
"pattern": "fixture-shape-b"
}
]
}