Skip to content

P3.34: ATO packet evidence index stale + exploit_id join key resolves to nothing - #75

Merged
franciszver merged 9 commits into
mainfrom
docs/p3-34-ato-evidence-index
Jul 26, 2026
Merged

P3.34: ATO packet evidence index stale + exploit_id join key resolves to nothing#75
franciszver merged 9 commits into
mainfrom
docs/p3-34-ato-evidence-index

Conversation

@franciszver

@franciszver franciszver commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Closes #68

Part A — docs/ATO_EVIDENCE_PACKET.md §5.2 dangling pointer

:29 cross-referenced §5.2 for VULN-0004, but §5.2 never mentioned it — a reader following that pointer landed on a paragraph describing a different, dismissed DoS recording (dos-overlong-query-max-query-chars, TRI-013) instead of VULN-0004's own evidence.

Fixed §5.2 to:

  • Name all five evals/recordings/ directories (not "a fourth recorded set exists" — verified by listing, there are five).
  • Add a dedicated VULN-0004 paragraph: EXP-0004, medium severity, evals/recordings/dos-unbounded-chat-message-length/1 draw (stated honestly, not the 3-draw sample size of the three criticals).
  • Explicitly separate VULN-0004's recording from the dismissed dos-overlong-query-max-query-chars probe (also 1 draw, not the 2 draws the issue brief guessed — verified by directory listing; it never produced a filed report).
  • Update the acceptance-check citation list (VULN-000{1,2,3}VULN-000{1,2,3,4}).

Part B — exploit_id join key

redteam/agents/documentation.py's docstring claimed a report's exploit_id is "the join key back to the full ExploitRecord ... in the exploit DB." Verified there is no committed exploit DB: tools/build_vuln_reports.py, tools/load_test_replay.py, and tools/run_campaign.py construct ExploitDB(":memory:"); tools/build_vuln_report_p3_54.py doesn't construct an ExploitDB at all (its own comment: "there is no persistent DB to add to"). All four report-builders construct DocumentationAgent(reports_dir=None). ExploitDB's own default is :memory:. EXP-0001..EXP-0004 resolve to nothing a reader can open.

(Correction, cold-review round 2: the original version of this paragraph overstated the audit — it claimed every report-builder "constructs ExploitDB(\":memory:\")", which is false for build_vuln_report_p3_54.py. The paragraph above is the corrected wording.)

Chose option 2 (soften the docstring) over committing an exploit-DB export: the durable evidence a reader actually needs already exists under evals/recordings/<probe>/, mapped from report to recording only via docs/ATO_EVIDENCE_PACKET.md §5.2 (see round 2 below — the report itself does not, and cannot, carry that mapping or repro steps) — inventing a new export artifact for four static records would be a heavier, redundant fix for a docs-scoped issue.

Red-first

tests/test_ato_evidence_index.py — derived from the filesystem (every evals/recordings/ dir, every docs/vuln_reports/*.json), so it self-updates as recordings/reports are added. Failing before the fix:

E       AssertionError: docs/ATO_EVIDENCE_PACKET.md §5.2 does not mention the following evals/recordings/ directories: ['dos-unbounded-chat-message-length'] -- every recording under evals/recordings/ must be referenced by name in §5.2 so a reader can map every committed recording to its disposition

Also adds a regression guard for the Part B join-key claim (verified it would have failed against the pre-fix docstring).

Cold-review round 2 (issue #68)

FIX 1 (BLOCKER). Part B's own fix introduced two NEW false claims: that durable evidence is "named directly on each report" (no committed VULN-*.json contains evals/recordings or recording at all — the schema forbids a recording_ref field), and that observed/expected "already carry the repro steps a reader needs" (they carry Judge.detect()'s detection signal — a label/message string — not an endpoint, payload, token, or case module). Rewrote redteam/agents/documentation.py's docstring to the true chain: durable evidence lives under evals/recordings/<probe>/; the report-to-recording mapping lives only in ATO §5.2; observed/expected carry the detection signal, not repro steps; the runnable repro is evals/cases/<case>.py plus the recording JSON. Extended tests/test_ato_evidence_index.py (Part C) with two new regex guards plus a ground-truth check against the actual report files. Red-first: verified both new patterns match the old (pre-round-2) docstring wording before it was rewritten.

FIX 2 (BLOCKER). docs/ATO_EVIDENCE_PACKET.md §5.3 said "the repo has since grown to 250 total with the sibling checkout present" — stale (self-contradicted §5.1's 343/237 pair at the time). Corrected to the live count. Widened tests/test_doc_test_counts.py with a fourth pattern (N total with the sibling checkout present) — the third distinct "N ... with the sibling checkout" phrasing to slip this guard. Verified red against the stale value, green against the corrected one.

FIX 3. docs/ARCHITECTURE.md listed "minimal repro" as a filed-report field in three places (§1 prose summary, §3(4) component list, §6 AI-use-disclosure table). vuln_report.schema.json is additionalProperties: false with no minimal_repro/recording_ref field — minimal_repro exists only on ExploitRecord, never persisted — contradicting documentation.py's own docstring section on this exact point. Corrected all three spots to name only real schema fields and point to the report + ATO §5.2 + recording chain.

One-liners.

  • ATO packet ~:435: label: "accepted_no_bound_observed" was presented as a top-level VULN-0004.json field; it's only inside observed prose. Matched the correct form used at ~:189.
  • tests/test_ato_evidence_index.py docstring claimed §5.2 "never mentioned VULN-0004" on main — false, it did (the "Re-verifying" paragraph, within §5.2's span). Reworded to the real defect (§5.2 named VULN-0004 without describing its evidence) and added a stronger co-occurrence test requiring each report ID to appear within 300 characters of an evals/recordings/ path in §5.2, not merely anywhere in the section. Verified it would have failed against the pre-Part-A docs (VULN-0004 was 2597 characters from the nearest recording mention there).
  • ATO packet §5.2 heading ("...plus VULN-0004") followed by an unscoped "All three are reproducible from..." — scoped to "All three criticals are reproducible from...".

Declutter (inline gate). Folded a duplicated "flag this claim unless a qualifying/negating phrase sits nearby" window-check into a shared _unqualified_claims() helper (Part B's join-key check and Part C's repro-steps check both needed it) — behavior-preserving, re-verified against the red-first proofs and full suite.

Self-caught in deep-review: the tests/test_doc_test_counts.py docstring I wrote for FIX 2 was internally contradictory ("a FOURTH phrasing... it is the third phrasing to slip this guard"). Reworded to disambiguate — third within the "N ... with the sibling checkout" family specifically, not counting the unrelated heading pattern.

Test count moved 343/237 → 346/240 across this round (5 test functions added net across Part C + the co-occurrence strengthening; skip count unchanged at 106) — cascaded through every "with the sibling checkout" / "N passed" claim in ATO_EVIDENCE_PACKET.md and DEMO_SCRIPT.md.

Gates (inline, full diff, both rounds)

  • declutter: round 1 — nothing to remove. Round 2 — one duplication found and folded (see above).
  • sec-audit: no findings, either round — diff is prose + docstring + test-only files; no entry points, no injection sinks, no auth logic touched.
  • deep-review: round 1 — no findings. Round 2 — one self-contradiction found in a docstring I wrote, fixed same pass; traced every corrected claim against the real schema, the real report JSON files, and the real evals/cases//evals/recordings/ filesystem contents.

Suite

pytest tests/test_doc_test_counts.py -q2 passed.

pytest tests/ -q346 passed (sibling Phase 2 checkout present locally).

Evidence integrity

docs/vuln_reports/*.json untouched across both rounds — sha256 verified identical before/after every commit on this branch (no main() of any report-building tool was run against the real directory).

What I found wrong in the issue brief / earlier PR description while verifying

  • dos-overlong-query-max-query-chars has 1 draw, not 2 as the issue brief guessed — confirmed by ls evals/recordings/dos-overlong-query-max-query-chars/ (one draw1 file + TRACE_RESOLUTION_NOTE.md). Documented the actual count.
  • This PR's own earlier description overstated the Part B audit (claimed build_vuln_report_p3_54.py constructs an ExploitDB; it doesn't). Corrected above.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TTi1zKDS1ajefLSmM9oL7E

…rding (#68)

evals/recordings/dos-unbounded-chat-message-length/ (VULN-0004's own live
evidence) is not referenced anywhere in docs/ATO_EVIDENCE_PACKET.md §5.2,
even though :29 points a reader there for VULN-0004. Derived from the
filesystem so it self-updates as recordings/reports are added.
…oin key honestly (#68)

Part A: §5.2 now names all five evals/recordings/ directories and all four
docs/vuln_reports/ reports. Adds VULN-0004's own dedicated paragraph (1 draw,
its own recording) and separates it clearly from the dismissed
dos-overlong-query-max-query-chars probe (also 1 draw, not the same evidence,
never filed a report). Fixes the "fourth recorded set" undercount (five
exist) and the acceptance-check citation list.

Part B (option 2): redteam/agents/documentation.py's docstring claimed
exploit_id resolves "in the exploit DB", but no report-builder in this repo
ever persists ExploitDB to disk (all use DocumentationAgent(reports_dir=
None); ExploitDB defaults to :memory:). Softened the docstring to state the
join key is in-process only and name the durable evidence that actually
exists: evals/recordings/<probe>/ and the filed docs/vuln_reports/<id>.json.
Chose this over committing an exploit-DB export because the durable evidence
already exists and is already cited elsewhere in this repo; inventing a new
export artifact for four static records would be a heavier, redundant fix
for a docs-scoped issue.

Extends tests/test_ato_evidence_index.py with a regression guard for the
join-key claim.
…ests (#68)

tests/test_ato_evidence_index.py adds 3 tests, moving the suite from
340/234 (with-sibling/CI) to 343/237. tests/test_doc_test_counts.py enforces
every documented count claim stays truthful -- updated every occurrence in
ATO_EVIDENCE_PACKET.md and DEMO_SCRIPT.md.
…ckout guard

§5.3's "the repo has since grown to 250 total with the sibling checkout
present" was stale -- self-contradicting §5.1's 343/237 (a 106 test
delta while §5.3 asserted a 13-test delta). Corrected to 343.

This is the third phrasing of "N ... with the sibling checkout" to slip
tests/test_doc_test_counts.py -- widen the guard with a fourth pattern
(N total with the sibling checkout present) and prove it now catches the
stale value (verified red against the reverted 250, green against 343).

Refs #68
….py docstring

The prior fix's replacement prose introduced two NEW false claims: that
durable evidence is "named directly on each report" (no committed
VULN-*.json contains "evals/recordings" or "recording" -- the schema
forbids a recording_ref field, and the report-to-recording mapping lives
only in ATO §5.2), and that observed/expected "already carry the repro
steps a reader needs" (they carry Judge.detect()'s label/message, not an
endpoint, payload, token, or case module).

Rewrote the docstring to the true chain: durable evidence lives under
evals/recordings/<probe>/, mapped from report to recording only in ATO
§5.2, with the runnable repro being evals/cases/<case>.py plus the
recording JSON.

Extended tests/test_ato_evidence_index.py (Part C) to catch both claim
shapes, plus a ground-truth check against the actual report files.
Red-first: verified both new patterns match the old (pre-this-commit)
docstring wording before the docstring was rewritten.

The two added test functions shifted the live suite count from 343/237
to 345/239 (skip count unchanged at 106) -- cascaded that count through
every "with the sibling checkout" claim in ATO_EVIDENCE_PACKET.md and
DEMO_SCRIPT.md so test_doc_test_counts.py stays green.

Refs #68
…RE.md

§3(4)/§1's report-field list and §6's AI-use-disclosure table both listed
"minimal repro" as a filed report field / independently-verified fact.
vuln_report.schema.json is additionalProperties:false with no
minimal_repro/recording_ref field -- minimal_repro exists only on
ExploitRecord, which no report-builder ever persists to disk. This
contradicted documentation.py's own module docstring section titled "Why
the vuln_report contract has no minimal_repro/recording_ref". Corrected
all three spots (prose summary, component list, AI-use table) to name
only the real schema fields and point to the report + ATO §5.2 +
recording chain instead.

Refs #68
…04 scope, test docstring

- ATO_EVIDENCE_PACKET.md: VULN-0004's label is not a top-level field --
  match the correct observed-field form used at ~:189.
- §5.2 heading said "the three ... plus VULN-0004" but the next sentence
  opened "All three are reproducible..." unscoped; now "All three
  criticals are reproducible...".
- tests/test_ato_evidence_index.py docstring claimed §5.2 "never
  mentioned VULN-0004" on main -- false, it did (the "Re-verifying"
  paragraph). Reworded to the real defect: §5.2 named VULN-0004 without
  describing its evidence. Added a stronger co-occurrence test requiring
  each report ID to appear near an evals/recordings/ path in §5.2, not
  merely anywhere in the section; verified it would have failed against
  the pre-fix docs (VULN-0004 was 2597 chars from the nearest recording
  mention, window is 300).

Test count moved to 346/240 (skip count unchanged) from this PR's added
tests -- cascaded through every doc claim again.

Refs #68
…fier check

Part C's repro-steps guard duplicated Part B's "flag this claim unless a
qualifying phrase sits nearby" window logic verbatim. Extracted
_unqualified_claims() and had both tests call it. Behavior-preserving:
re-verified red-first proof against the old docstring wording still
fails, full suite still 346 passed.
…y phrasing count

The module docstring said "a FOURTH phrasing" in one clause and "it is
the third phrasing to slip this guard" in the next -- self-contradictory.
Reworded: it's the third distinct "N ... with the sibling checkout"
phrasing specifically (after "move it to N with..." and "N tests
with..."), not counting the unrelated "N-test suite" heading pattern.
@franciszver
franciszver merged commit 83fb4ad into main Jul 26, 2026
1 check passed
@franciszver
franciszver deleted the docs/p3-34-ato-evidence-index branch July 26, 2026 03:20
franciszver added a commit that referenced this pull request Jul 26, 2026
…, upstream filings, and kickoff-constraint gap (issue #59)

Extends tests/test_release_notes.py red-first with the corrected facts,
then rewrites docs/RELEASE_NOTES_v3.0.0.md to match:

- #63 and #68 are CLOSED (P3.31/P3.34) -- rewrites the limitations
  section as gaps found and closed, not open, and adds the three new
  limitations P3.31 introduced (pending_human_triage_count is per-run
  not directory-wide, --approve's --db-path opt-out, --reports-dir
  requiring --db-path).
- All four findings are now filed upstream (#167-#170); corrects the
  false "no upstream issue for VULN-0001/2/3" paragraph and notes
  #169/#170 as evidence against upstream #130's closure premise,
  without demanding a reopen.
- Owns the kickoff-brief hard-constraint gap explicitly: intent (no
  shared context) is met; the parenthesised OS-process mechanism is
  not, per ARCHITECTURE.md and the ATO packet, both already public.
- Corrects the stale "redteam.observability not in the forbidden
  import set" claim -- it now is (test_judge_agent.py), and adds the
  symmetric Red-Team-side AST scan.
- Re-derives test counts post-rebase (405 with sibling / 299 in CI,
  106 skipped) across RELEASE_NOTES/ATO_EVIDENCE_PACKET/DEMO_SCRIPT.

Rebase note: docs/ATO_EVIDENCE_PACKET.md and docs/DEMO_SCRIPT.md
conflicts were resolved by taking main's post-#74/#75/#76 versions
wholesale (verified main's wording already supersedes this branch's
pre-rebase edits to the same passages), then bumping only the test
counts to the new post-rebase live total.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTi1zKDS1ajefLSmM9oL7E
franciszver added a commit that referenced this pull request Jul 26, 2026
… by the rebase

judge.py's (case, response, attempt_id) triple was cited at
judge.py:96-100,343 -- score()'s actual signature moved to 348-357 as
other main PRs (#74/#75/#76) touched the file. campaign.py's
try:-guarded component calls were cited at ...,436 for the
documentation.file_report try: block -- it moved to 446. Both verified
against the current file on disk before correcting; no other citation
in the doc drifted (spot-checked judge.py:44-47, chat.py:570-594,
the recording path, and the JSON-report field claims against the live
tree -- all still accurate).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTi1zKDS1ajefLSmM9oL7E
franciszver added a commit that referenced this pull request Jul 26, 2026
…, upstream filings, and kickoff-constraint gap (issue #59)

Extends tests/test_release_notes.py red-first with the corrected facts,
then rewrites docs/RELEASE_NOTES_v3.0.0.md to match:

- #63 and #68 are CLOSED (P3.31/P3.34) -- rewrites the limitations
  section as gaps found and closed, not open, and adds the three new
  limitations P3.31 introduced (pending_human_triage_count is per-run
  not directory-wide, --approve's --db-path opt-out, --reports-dir
  requiring --db-path).
- All four findings are now filed upstream (#167-#170); corrects the
  false "no upstream issue for VULN-0001/2/3" paragraph and notes
  #169/#170 as evidence against upstream #130's closure premise,
  without demanding a reopen.
- Owns the kickoff-brief hard-constraint gap explicitly: intent (no
  shared context) is met; the parenthesised OS-process mechanism is
  not, per ARCHITECTURE.md and the ATO packet, both already public.
- Corrects the stale "redteam.observability not in the forbidden
  import set" claim -- it now is (test_judge_agent.py), and adds the
  symmetric Red-Team-side AST scan.
- Re-derives test counts post-rebase (405 with sibling / 299 in CI,
  106 skipped) across RELEASE_NOTES/ATO_EVIDENCE_PACKET/DEMO_SCRIPT.

Rebase note: docs/ATO_EVIDENCE_PACKET.md and docs/DEMO_SCRIPT.md
conflicts were resolved by taking main's post-#74/#75/#76 versions
wholesale (verified main's wording already supersedes this branch's
pre-rebase edits to the same passages), then bumping only the test
counts to the new post-rebase live total.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTi1zKDS1ajefLSmM9oL7E
franciszver added a commit that referenced this pull request Jul 26, 2026
… by the rebase

judge.py's (case, response, attempt_id) triple was cited at
judge.py:96-100,343 -- score()'s actual signature moved to 348-357 as
other main PRs (#74/#75/#76) touched the file. campaign.py's
try:-guarded component calls were cited at ...,436 for the
documentation.file_report try: block -- it moved to 446. Both verified
against the current file on disk before correcting; no other citation
in the doc drifted (spot-checked judge.py:44-47, chat.py:570-594,
the recording path, and the JSON-report field claims against the live
tree -- all still accurate).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTi1zKDS1ajefLSmM9oL7E
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P3.34: ATO packet evidence index is stale and exploit_id join keys resolve to nothing

1 participant