fix(llm-security): scanner false-positives/negatives — trigger, toxic-flow, policy-loader (#38-#41,#57,#26)

#38 toxic-flow matched trifecta-leg keywords with bare includes(), so substrings ('url' in 'curl', 'key' in 'monkey', 'auth' in 'author') fabricated CRITICAL trifectas on benign components; now word-boundary matched. #40 TRG-broad fired HIGH on a bare any/all/every anywhere ('fix any lint errors'); the universal-claim regex now requires genuine universal phrasing. #41 TRG-baiting substring-matched ('any file' in 'many files'); now boundary-anchored. #39 the broad-name list missed multi-char generic names (helper/assistant/auto/general/agent/tool); widened coherently so it does not reintroduce #40. #57 the '(recovered from obfuscation)' label compared raw against a lowercased normal form, firing on any uppercase char; now gated on an explicit decode-changed flag.

#26 (same file) getPolicyValue used 'key in sectionObj' with no type guard, so a scalar section override in policy.json (e.g. {"injection":"block"}) threw an uncaught TypeError; now guarded to fall back to the default. Suite 1931/0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01TcQyMTQfyrsAapaCMPxTtQ
This commit is contained in:
Kjell Tore Guttormsen 2026-07-18 10:15:10 +02:00
commit f3aaf5479f
6 changed files with 339 additions and 13 deletions

View file

@ -129,6 +129,14 @@ describe('policy-loader', () => {
const defaults = getDefaultPolicy();
assert.equal(defaults.trifecta.escalation_window, 5);
});
it('getPolicyValue survives a scalar section override without throwing (#26)', () => {
// A user writing {"injection": "block"} (scalar instead of object) must
// not crash the "key in sectionObj" lookup with a TypeError.
writeFileSync(POLICY_FILE, JSON.stringify({ injection: 'block' }));
const val = getPolicyValue('injection', 'mode', 'warn', TEST_ROOT);
assert.equal(val, 'warn');
});
});
// ---------------------------------------------------------------------------