WIP: Harden booklet validator contract #5

Closed
clawlter wants to merge 9 commits from audit/validator-contract into main
Owner

Scope

Hardens booklet validation around semantic completeness, normalized cross-artifact reconciliation, strict canonical HTTPS/QR policy, path containment, and decoded image integrity. Adds focused behavior fixtures before implementation, requires non-empty source candidate websites while respecting the generator's explicit website override, and excludes commented TeX commands from semantic reconciliation and asset/imposition scans.

No political roster or candidate content was changed.

CI root cause and remediation

Forgejo run 19 on 0703207 failed during make generate: the network-backed generator could not resolve bentrumanforcongress.com, so generation exited before validator or PDF build steps. The branch now contains signed merge commit ec000a8, which integrates committed, content-addressed Git LFS media and its digest manifest. Normal generate, validate, and build resolve reviewed local media only; remote fetching is isolated to the explicit make refresh-media operation.

Verification

  • git lfs pull completed
  • make generate passed
  • make test passed: 32 tests
  • make validate passed
  • make build passed: 8-page digital and 4-page imposed print PDFs
  • uv run --offline --with ruff ruff check scripts/validate_booklet.py tests/test_validate_booklet.py passed
  • python3 -m py_compile scripts/validate_booklet.py tests/test_validate_booklet.py scripts/generate_booklet.py scripts/media_inputs.py scripts/refresh_media.py passed
  • git diff --check and generated-source reproducibility checks passed
  • Signed merge commit was verified with git verify-commit

Live CI and integration status

  • Exact head ec000a8bafc1ec22efa620b9d9afac45008a65b4 is green: the sole required Booklet CI / Validate and build booklet PDFs (pull_request) status completed successfully in 18m45s at Forgejo Actions run 22 (internal run 3551), 2026-07-12 20:35 ET.
  • Forgejo's jobs/log endpoint for that completed run still returns HTTP 404, so this result is recorded from the authoritative commit-status endpoint rather than job-log retrieval.
  • Forgejo PR API still reports mergeable: false, but origin/main (5058059) is an ancestor of the exact head and git merge-tree --write-tree origin/main ec000a8 is clean. No merge was attempted; this remains a Forgejo integration-state discrepancy, not a Git conflict.

Review

Independent review requested from @mehalter after the final CI result.

Tracks kanban task t_d9dd0c1b.

## Scope Hardens booklet validation around semantic completeness, normalized cross-artifact reconciliation, strict canonical HTTPS/QR policy, path containment, and decoded image integrity. Adds focused behavior fixtures before implementation, requires non-empty source candidate websites while respecting the generator's explicit website override, and excludes commented TeX commands from semantic reconciliation and asset/imposition scans. No political roster or candidate content was changed. ## CI root cause and remediation Forgejo run 19 on `0703207` failed during `make generate`: the network-backed generator could not resolve `bentrumanforcongress.com`, so generation exited before validator or PDF build steps. The branch now contains signed merge commit `ec000a8`, which integrates committed, content-addressed Git LFS media and its digest manifest. Normal `generate`, `validate`, and `build` resolve reviewed local media only; remote fetching is isolated to the explicit `make refresh-media` operation. ## Verification - `git lfs pull` completed - `make generate` passed - `make test` passed: 32 tests - `make validate` passed - `make build` passed: 8-page digital and 4-page imposed print PDFs - `uv run --offline --with ruff ruff check scripts/validate_booklet.py tests/test_validate_booklet.py` passed - `python3 -m py_compile scripts/validate_booklet.py tests/test_validate_booklet.py scripts/generate_booklet.py scripts/media_inputs.py scripts/refresh_media.py` passed - `git diff --check` and generated-source reproducibility checks passed - Signed merge commit was verified with `git verify-commit` ## Live CI and integration status - Exact head `ec000a8bafc1ec22efa620b9d9afac45008a65b4` is green: the sole required `Booklet CI / Validate and build booklet PDFs (pull_request)` status completed successfully in 18m45s at Forgejo Actions run 22 (internal run 3551), 2026-07-12 20:35 ET. - Forgejo's jobs/log endpoint for that completed run still returns HTTP 404, so this result is recorded from the authoritative commit-status endpoint rather than job-log retrieval. - Forgejo PR API still reports `mergeable: false`, but `origin/main` (`5058059`) is an ancestor of the exact head and `git merge-tree --write-tree origin/main ec000a8` is clean. No merge was attempted; this remains a Forgejo integration-state discrepancy, not a Git conflict. ## Review Independent review requested from @mehalter after the final CI result. Tracks kanban task `t_d9dd0c1b`.
test: enforce booklet validator contract
Some checks failed
Booklet CI / Validate and build booklet PDFs (pull_request) Failing after 7m2s
8424be0be5
clawlter force-pushed audit/validator-contract from 8424be0be5
Some checks failed
Booklet CI / Validate and build booklet PDFs (pull_request) Failing after 7m2s
to 4275830301
Some checks failed
Booklet CI / Validate and build booklet PDFs (pull_request) Failing after 5m40s
2026-07-12 16:11:34 -04:00
Compare
clawlter changed title from test: enforce booklet validator contract to WIP: Harden booklet validator contract 2026-07-12 16:12:49 -04:00
fix: close booklet validator review gaps
Some checks failed
Booklet CI / Validate and build booklet PDFs (pull_request) Failing after 8m1s
0703207e85
fix(booklet): pin reviewed media inputs
All checks were successful
Booklet CI / Validate and build booklet PDFs (pull_request) Successful in 8m1s
e97e8a1d2b
fix(booklet): refresh media from source bytes
All checks were successful
Booklet CI / Validate and build booklet PDFs (pull_request) Successful in 7m55s
2546bc85af
fix(booklet): persist fetched QR provenance
All checks were successful
Booklet CI / Validate and build booklet PDFs (pull_request) Successful in 18m27s
44fddd79a8
merge: pin deterministic booklet media
All checks were successful
Booklet CI / Validate and build booklet PDFs (pull_request) Successful in 18m45s
ec000a8baf
mehalter approved these changes 2026-07-12 19:11:52 -04:00
Dismissed
mehalter approved these changes 2026-07-13 07:00:23 -04:00
Dismissed
fix: reject animated booklet media
Some checks failed
Booklet CI / Validate and build booklet PDFs (pull_request) Has been cancelled
3fc448f839
revert: remove content edits from validator PR
Some checks failed
Booklet CI / Validate and build booklet PDFs (pull_request) Has been cancelled
25b6eb2bc9
Author
Owner

Validator/content-scope blocker remediation is pushed for independent review.

Signed logical commits:

  • 3fc448f839df5c50b14b5fe83d8064171c26e704 — reject animated/multi-frame media
  • 25b6eb2bc93c0c002b3c1ec6d39840e200dd0588 — remove political/content edits from this validator PR

Still-image contract evidence:

  • Focused RED before implementation: generated four-frame GIF and four-frame Motion JPEG were accepted; generated one-frame GIF with a loop declaration was accepted (2 test methods failed across those cases).
  • Focused GREEN after implementation: animated GIF/MJPEG rejection, one-frame GIF/APNG animation-declaration rejection, and ordinary one-frame PNG/GIF/WebP acceptance all pass.
  • Direct adversarial probe: ffprobe_nb_read_frames=4; validate_still_image=False.
  • The validator now asks ffprobe to count the complete selected stream, requires exactly one decoded frame, rejects GIF/APNG/WebP animation declarations, and uses ffmpeg -xerror to decode the full selected stream rather than stopping after frame 1.

Verification at head 25b6eb2:

  • make test: 35 tests passed.
  • ruff check scripts/validate_booklet.py tests/test_validate_booklet.py: passed.
  • python3 -m compileall -q scripts tests: passed.
  • git diff --check: passed.
  • data/ballot_data.json, data/render_data.json, and data/shortio-candidate-links.json exactly match fetched origin/main; they are absent from the PR diff.

Practical content gate after restoring current input:

  • make generate: exit 0.
  • python3 scripts/validate_booklet.py: exit 1, correctly reporting legacy non-HTTPS entries for the Dana Barrett, Penny Brown Reynolds, and Shelia Edwards links (including the corresponding short-link records).
  • make: exit 2 at the validation prerequisite for the same reason; PDF compilation is intentionally blocked.

Those URLs now require politically authorized input remediation. This validator-only task deliberately does not alter them or weaken the HTTPS/QR contract.

Forgejo CI was still pending / Waiting to run after a bounded five-minute poll, consistent with shared-runner queue contention.

Validator/content-scope blocker remediation is pushed for independent review. Signed logical commits: - `3fc448f839df5c50b14b5fe83d8064171c26e704` — reject animated/multi-frame media - `25b6eb2bc93c0c002b3c1ec6d39840e200dd0588` — remove political/content edits from this validator PR Still-image contract evidence: - Focused RED before implementation: generated four-frame GIF and four-frame Motion JPEG were accepted; generated one-frame GIF with a loop declaration was accepted (2 test methods failed across those cases). - Focused GREEN after implementation: animated GIF/MJPEG rejection, one-frame GIF/APNG animation-declaration rejection, and ordinary one-frame PNG/GIF/WebP acceptance all pass. - Direct adversarial probe: `ffprobe_nb_read_frames=4`; `validate_still_image=False`. - The validator now asks ffprobe to count the complete selected stream, requires exactly one decoded frame, rejects GIF/APNG/WebP animation declarations, and uses ffmpeg `-xerror` to decode the full selected stream rather than stopping after frame 1. Verification at head `25b6eb2`: - `make test`: 35 tests passed. - `ruff check scripts/validate_booklet.py tests/test_validate_booklet.py`: passed. - `python3 -m compileall -q scripts tests`: passed. - `git diff --check`: passed. - `data/ballot_data.json`, `data/render_data.json`, and `data/shortio-candidate-links.json` exactly match fetched `origin/main`; they are absent from the PR diff. Practical content gate after restoring current input: - `make generate`: exit 0. - `python3 scripts/validate_booklet.py`: exit 1, correctly reporting legacy non-HTTPS entries for the Dana Barrett, Penny Brown Reynolds, and Shelia Edwards links (including the corresponding short-link records). - `make`: exit 2 at the validation prerequisite for the same reason; PDF compilation is intentionally blocked. Those URLs now require politically authorized input remediation. This validator-only task deliberately does not alter them or weaken the HTTPS/QR contract. Forgejo CI was still `pending` / `Waiting to run` after a bounded five-minute poll, consistent with shared-runner queue contention.
fix: reject multi-stream still images
Some checks failed
Booklet CI / Validate and build booklet PDFs (pull_request) Failing after 15m58s
4e9a948d23
Author
Owner

Narrow multi-stream still-image remediation pushed at signed head 4e9a948d23c6423d4429bc0a067d665c67ce22a5.

  • scripts/validate_booklet.py now probes every video stream (-select_streams v), rejects containers unless exactly one video stream with exactly one decoded frame is present, and fully decodes all accepted video streams (-map 0:v).
  • tests/test_validate_booklet.py generates a Matroska payload saved with a .png name containing two independent one-frame PNG streams and proves rejection.

Verification:

  • Focused TDD RED: the new test failed at prior head because the validator returned True.
  • Focused GREEN: 4 still-image behavior tests passed (multi-stream; multi-frame GIF/MJPEG; GIF/APNG animation declaration; ordinary one-frame PNG/GIF/WebP).
  • make test: 36 tests passed in 19.209s after git lfs install --local && git lfs pull.
  • Ruff, Python compileall, git diff --check, and merge-tree against fetched origin/main passed.
  • Direct all-stream ffprobe showed two PNG video streams with nb_read_frames=1 each; validator returned False.
  • Exact two-dot and three-dot diffs for the three forbidden data files remain empty.
  • Practical disposable-copy run: make generate passed; validation and build stopped at the expected legacy-HTTP gate for Dana Barrett, Penny Brown Reynolds, and Shelia Edwards without content edits or policy weakening.

Please re-review the new signed head. The new exact-head Forgejo run is queued / Waiting to run; this is shared-runner contention, not a code failure.

Narrow multi-stream still-image remediation pushed at signed head `4e9a948d23c6423d4429bc0a067d665c67ce22a5`. - `scripts/validate_booklet.py` now probes every video stream (`-select_streams v`), rejects containers unless exactly one video stream with exactly one decoded frame is present, and fully decodes all accepted video streams (`-map 0:v`). - `tests/test_validate_booklet.py` generates a Matroska payload saved with a `.png` name containing two independent one-frame PNG streams and proves rejection. Verification: - Focused TDD RED: the new test failed at prior head because the validator returned `True`. - Focused GREEN: 4 still-image behavior tests passed (multi-stream; multi-frame GIF/MJPEG; GIF/APNG animation declaration; ordinary one-frame PNG/GIF/WebP). - `make test`: 36 tests passed in 19.209s after `git lfs install --local && git lfs pull`. - Ruff, Python compileall, `git diff --check`, and merge-tree against fetched `origin/main` passed. - Direct all-stream ffprobe showed two PNG video streams with `nb_read_frames=1` each; validator returned `False`. - Exact two-dot and three-dot diffs for the three forbidden data files remain empty. - Practical disposable-copy run: `make generate` passed; validation and build stopped at the expected legacy-HTTP gate for Dana Barrett, Penny Brown Reynolds, and Shelia Edwards without content edits or policy weakening. Please re-review the new signed head. The new exact-head Forgejo run is queued / Waiting to run; this is shared-runner contention, not a code failure.
mehalter left a comment

Ci/cd isn't passing

Ci/cd isn't passing
Author
Owner

Closing without merge because the owner cancelled the code-security-audit-2026-07 campaign. This does not revert previously merged work.

Closing without merge because the owner cancelled the `code-security-audit-2026-07` campaign. This does not revert previously merged work.
clawlter closed this pull request 2026-07-14 08:16:24 -04:00
Some checks failed
Booklet CI / Validate and build booklet PDFs (pull_request) Failing after 15m58s

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
clawlter/chamblee-2026-booklet!5
No description provided.