Repository navigation
feat: let validate check a bundle against a digest from outside it - #38
Merged
utsab345 merged 1 commit intoOct 30, 2026
Merged
Conversation
Owner
|
Thanks @how2how2how2-arch for this. I verified it locally, approved it, and merged it. Appreciate the thorough tests and clear docs. |
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.
What this adds
stepfork validate --expect-digest <sha256>— check a bundle against thebundle_digestthatstepfork run --receiptrecorded, i.e. a digest from outside the bundle.Why
validate --verify-integritychecks each payload file against theintegrity.jsonthe bundle carries. That catches a file corrupted or edited on its own. It cannot catch a bundle whose record was rewritten to match the payload: the three SHA-256 values are recomputable by whoever editsevents.jsonl, and the bundle then verifies clean. A file the record does not name is invisible to it too. So once a bundle travels — CI artifact, a colleague, a bug report —VERIFIEDsays the record agrees with the payload, not that this is the bundle a run produced.export/receipt.py::bundle_digestalready hashes every file in the bundle in sorted relative-path order, andstepfork runalready writes it into the receipt. It was write-only: recorded, never checkable. This exposes the check without a second implementation of the standard —_enforce_expected_digestcalls the samebundle_digestthe receipt does, andis_sha256_digestintrace/integrity.pyreplaces the private regex use in_validate_recordso the format is defined once.Behaviour
0Bundle digest: MATCH <digest>1integrity.jsondoes not rule this out--expect-digestis not 64 hex characters2--expect-digestExit
2for a malformed digest is deliberate: a check that could not run must not share an exit code with a check that ran and failed. Digests are trimmed and lower-cased before comparison, so a value copied out of JSON needs no cleanup.Tests
verify_bundle_integrity(...).status is VERIFIED, so the gap is demonstrated rather than assumed.integrity.json→verify_bundle_integrityreportsVERIFIED, and--expect-digestcatches it2--helplists the flagBoth catch-tests were run against a neutered comparison (
if True:in place ofhmac.compare_digest): exactly those two go red and the match-tests stay green.Checks
uv run pytest706 passed ·uv run ruff check .·uv run ruff format --check .(196 files) ·uv run mypy(132 source files) ·uv build·uv run mkdocs build --strict· the three demos, eachbuggy FAIL (rc=1), fixed PASS.Scope
Additive: no existing flag, exit code or output changes.
docs/cli.mddocuments the flag and what the check does not prove — a digest that arrives inside the same archive as the bundle proves nothing, so it has to travel by a route the bundle's author cannot edit.This is not tied to an issue; if you would rather it started as one, say so and I will move the discussion there.