fix(exaforce): let LLM verdict override static severity floor - #15
Open
will-exaforce wants to merge 1 commit into
Open
fix(exaforce): let LLM verdict override static severity floor#15will-exaforce wants to merge 1 commit into
will-exaforce wants to merge 1 commit into
Conversation
Upstream's meta-analyzer keeps CRITICAL/HIGH findings the LLM rejected, tagged llm-unconfirmed. In production that surfaces regex noise the LLM already dismissed. Emptying the floor entirely cost recall: the floor was mostly shielding the semantic analyzers' LLM findings from a second LLM pass, and the meta-analyzer vetoed CRITICAL findings on real droppers. Keep the floor only for LLM-backed findings (SQP/SDI/SSD, TP4); every static finding now follows the meta-analyzer verdict. Mode is selectable per call via SKILLSPECTOR_META_SEVERITY_FLOOR=none|semantic|upstream (default semantic). Same-day A/B/C on 188 borderline unit-scans: upstream 68 TP/10 FP, semantic 65 TP/7 FP, none 54 TP/3 FP. Four upstream TestApplyFilterSeverityFloor tests now fail by design; recorded in docs/superpowers/EXPECTED_TEST_FAILURES.md, left untouched to avoid upstream-sync conflicts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PE3,P1,YR4,TP4,E2,RA1, …) on benign skills.SQP-*,SDI-*,SSD-*) andTP4, whichmcp_tool_poisoningderives from achat_completionreply. Upstream's floor was mostly shielding those first-pass LLM verdicts from a second LLM pass; removing it wholesale let the meta-analyzer veto CRITICAL findings on real droppers andexec()payloads.SKILLSPECTOR_META_SEVERITY_FLOOR=none|semantic|upstream(defaultsemantic), read on everyapply_filtercall so it can be flipped at runtime for in-process A/Bs as well as per benchmark run.src/skillspector/exaforce/; no upstream-tracked file is modified.Details
Why the floor is not simply removed. Every analyzer's findings flow into the meta-analyzer, whose prompt labels them all "static analysis findings". Corpus-wide on MalSkillBench the semantic rules produce ~2× as many HIGH/CRITICAL findings as all static rules combined, so upstream's floor (
src/skillspector/nodes/meta_analyzer.py:376, applied at:455) was primarily an LLM-vs-LLM tiebreak. Same-day controlled re-scan of the 94 borderline units a full 900-unitnonerun got wrong, two replicates each (188 unit-scans per arm),nvidia.nemotron-super-3-120b:upstreamsemantic(this PR's default)noneSpot-checked
nonelosses included a base64 PowerShell download-and-execute and Fernet-decryptedexec()insetup.py, flagged CRITICAL at confidence ≥ 0.9 by the semantic analyzers and then dropped by the meta-analyzer. Spot-checkedsemanticdrops of staticRA1/AS1hits were 11/12 regex misfires on benign text ("never modify your own rules", tutorial heredocs), including on malware units whose payload lived elsewhere.Patch.
apply()insrc/skillspector/exaforce/_filter_patches.pywrapsLLMMetaAnalyzer.apply_filteronce at import. Each call resolves the mode and dispatches:upstreamcalls through;noneshadowsself._HIGH_SEVERITY_FLOORwith an empty set for one call;semanticruns upstream's filter twice — floor on for LLM-backed findings, floor off for static — then restores input order byfinding_id. Drift guards raisePatchDriftErrorat import if the floor's value changes,apply_filterdisappears, or upstream stops reading the floor viaself.(the shadowing depends on that access path).Findingcarries no source-analyzer field, sois_llm_findingmatches normalized rule ids (LLM_RULE_PREFIXES+LLM_RULE_IDS); the benchmark corpus shows rare LLM-emitted variants likessd-2, hence the case-insensitive strip. A warning is logged when a batch returns zero verdicts for a file that had findings, since undernone/semanticthat drops the file's static findings where upstream kept CRITICAL/HIGH.Tests.
tests/exaforce/test_patches.pyruns one subprocess per mode against a mixed static/LLM batch (explicit denial, omission, confirmation, plus an omittedTP4), and one that flips the env var at runtime and checks instance state is restored. Four upstream tests inTestApplyFilterSeverityFloornow fail by design (they assert the static floor), on top of the four schema-pruning tests that already did;docs/superpowers/EXPECTED_TEST_FAILURES.mdis updated. Per fork policy those upstream tests are left untouched — no deselect/xfail — so CI stays red by design.Tests: 1917 passed, 8 failed by design (
uv run pytest);ruff check,ruff format --check, andmypyclean on the new module.Implementation Plan
Context. Fork policy is "trust the LLM over static". Upstream's CRITICAL/HIGH floor in the meta-analyzer keeps static findings the LLM rejected, producing noise in production. A first attempt emptied the floor entirely; a 900-unit benchmark showed −24 TP / +8 TN, and investigation traced the recall loss to the meta-analyzer overruling the semantic analyzers rather than to static rules.
Approach. Keep the floor only where it arbitrates between two LLMs; drop it wherever a static rule disagrees with the LLM. Expose the three policies as an env-selected mode so the benchmark can run controlled arms.
src/skillspector/exaforce/_filter_patches.pyapply_filterwrapper, drift guardssrc/skillspector/exaforce/__init__.py_filter_patches.apply()tests/exaforce/test_patches.pydocs/superpowers/EXPECTED_TEST_FAILURES.mdVerification. Fork tests 14/14; mypy clean on the new module; a fresh-context
/code-reviewpass produced 8 findings, all addressed (LLM-backedTP4coverage, per-call mode resolution, source-access drift guard, empty-verdict warning, mypy) or explicitly declined (xfail of upstream tests — conflicts with fork policy). Full suite 1917 passed / 8 by-design upstream failures. Same-day benchmark A/B/C on--from-run <head> --failures-only, two replicates per arm (runs864ccf78be3b,a5c5a221a8c6,f5aaf9771d20,2310deadaad9,729b41e62cfc,838e2a2288ccinbenchmark/benchmark.db). Still outstanding: a same-day full 900-unitsemanticvsupstreampair before treating the magnitude as settled.