feat(adjudication): show the judge only reachable (loaded-at-runtime) CVEs (JEF-453)#252
Merged
Merged
Conversation
… CVEs (JEF-453)
The recurring false `exploitable` (JEF-451) is the judge fabricating a
`[reachability: loaded-at-runtime]` tag onto a CVE that is actually not-observed.
The fable audit's root cause R1: `loaded-at-runtime` is the most-primed phrase in
the prompt (~10x in instructions, 0x in evidence), and the prompt showed the judge
not-observed CVEs — a non-evidence target to relabel.
Fix (evidence-shaping, prompt layer): the judge's CVE field carries ONLY
`loaded-at-runtime` CVEs — the sole CVE category that is exploitation evidence.
not-observed / present-static-binary / unknown-reachability CVEs are context ("how
bad IF exploited"), kept on the dashboard for operators but omitted from the judge
prompt (filter in `render_evidence`; the anti-fabrication guards keep their full
view, so their behaviour is unchanged). If none are reachable → "(none)". This lets
the two now-moot "trap" instructions be deleted, shrinking the prompt.
This removes NON-evidence, not the objective-breadth capping ADR-0029 forbids (a
not-observed CVE can never change a correct verdict). Recorded as an ADR-0029
scope-note.
Validated on the deployed qwen3:1.7b, A/B old-vs-new:
- temp-0.8 boundary mass on the protector flip prompt: OLD 3/20 (15%) -> NEW 0/17 (0%).
- no false negatives: log4j (loaded-at-runtime) 5/5 exploitable; live-signal
exploitable; exposed-secret breach 5/5 (4 exploitable + 1 confirmed, both
breach-positive).
Also:
- Bakeoff resynced: SYS mirrors the new engine prompt; the all-not-observed
fixtures' CVE field -> "(none)"; added a `--temp` boundary-mass A/B mode.
- Prompt-content tests updated (critical_cve fixture is now loaded-at-runtime so it
survives the filter; the not-observed/static-binary tests assert OMISSION).
- gitignore /scratchpad/ and untrack an audit report that leaked in via git add -A.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP
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.
Closes JEF-453. The source-level fix for the recurring false
exploitable— the judge fabricating aloaded-at-runtimetag onto a not-observed CVE.The change
The judge's CVE field now carries only
[reachability: loaded-at-runtime]CVEs — the sole CVE category that is exploitation evidence.not-observed/present-static-binary/unknown-reachability CVEs are context ("how bad IF exploited"), kept on the dashboard for operators but omitted from the judge prompt. If none are reachable →(none).prompt.rs::render_evidence(prompt layer only). The anti-fabrication guards (guard_fabricated_cve,guard_fabricated_reachability_tag) keep their full-evidence view viamodel_call, so their behaviour is unchanged.loaded-at-runtimeis the most-primed phrase; the judge no longer sees a not-observed CVE to relabel). The two now-moot "trap" instructions are deleted, shrinking the prompt.Why this is ADR-0029-compliant
ADR-0029's evidence-capping prohibition protects the reachable-objectives breadth (breadth is part of the judgement; hiding objectives could change a correct verdict). A not-observed CVE is different: by the prompt's own definition it is context, never evidence, so omitting it from the judge cannot hide a breach. This is "don't feed the judge non-evidence," not "cap the evidence" — the objective list is still shown in full. Recorded as an ADR-0029 scope-note.
Validation (deployed qwen3:1.7b, A/B old-vs-new)
The false-positive boundary mass collapses 15% → 0% with zero false negatives. The one wobble (exposed-secret occasionally
confirmednotexploitable) is breach-positive and harmless in shadow.Also
(none), and a--tempboundary-mass A/B mode added (the sensitive metric for a rare tail). Resynced protector case refutes 3/3.critical_cvefixture is now loaded-at-runtime so it survives the filter; the not-observed / static-binary tests now assert omission from the judge prompt./scratchpad/and untrack an audit report that leaked in via agit add -A.Ships as v0.3.107, after v0.3.105 (JEF-445 unfreeze) and v0.3.106 (JEF-451 guard) — the prompt fix, its deterministic backstop, and the unfreeze together close this false-positive class.
🤖 Generated with Claude Code