Skip to content

Check a trusted receipt key as a curve point, and hold a run's retained answer to its signed record (#52, #53) - #58

Merged
kikashy merged 2 commits into
mainfrom
fix/receipt-key-and-disposition
Oct 9, 2026
Merged

kikashy merged 2 commits into
mainfrom
fix/receipt-key-and-disposition

Conversation

@kikashy

@kikashy kikashy commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Two HIGH findings of the cross-vendor audit of the chain of runs at v0.6.0 (tracking #57).

What changes

Tests

The signed, witnessed version-5 vector passes unchanged; the same export with only the answer changed, missing or null fails with and without --runtime; the three malformed pins are refused; the help and guide sentences are asserted. Scratch mutations removing the key validation, the disposition binding, a help line or a documented unsigned member each failed a named test.

Checks

Drafted with an OpenAI model (Codex CLI) in a scratch clone (gofmt, vet, the socket-free tests: internal/runner 136 passed, cmd/jpack-runner 24 passed). Committed by the coordinator after running, under the machine lock: go vet ./..., go test ./... with sockets, and -race on internal/runner. CI runs the rest with the pinned runtime.

Review

Material: what a verification establishes. A cross-vendor round follows, run by the coordinator; the drafting model was OpenAI's, so the reviewer is Anthropic's.

🤖 Generated with Claude Code

…ed answer to its signed record (#52, #53)

A mapping profile's trusted receipt public key is parsed with the same curve,
canonical-form and small-order checks Runner applies to its own record
signatures, so a malformed pin can no longer authenticate a forged
acquisition. verify-run always compares the export's retained disposition
with the disposition in the signed audit record, before either input path
and independently of the optional replay, and fails with
audit-disposition-mismatch when they differ; the help and MAPPING-V2 name
every export member the signature does not cover. Found by a cross-vendor
audit of the chain of runs (runner #57); drafted with an OpenAI model (Codex)
and verified by the coordinator's runs of the full test suite with sockets.

Closes #52, closes #53

Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@kikashy

kikashy commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Cross-vendor round started on head 214f279 (independent Claude reviewer — Anthropic — fresh context; the drafting model was OpenAI's, so this is the different-vendor round the interim review regime requires). One round; findings that change what the verifier establishes are fixed before merge, the rest filed.

@kikashy

kikashy commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Round 1 result and dispositions

Reviewed SHA: 214f279. Reviewer: Anthropic (Claude Opus, independent, fresh context); the drafting model was OpenAI's. It ran six vectors across nine flag combinations under the published 0.6.0 and this head (same exit codes; output byte-identical apart from the intended wording), the twenty named tests, and two compiling mutations, each killed by named tests. Verdict: MERGEABLE AFTER LISTED FIXES — no HIGH.

# Severity Finding Disposition
1 MEDIUM The new check reads run.result.disposition by exact name only; an export with an added "Disposition" member (a different case) passes every check and reports matches-audit, and a Go encoding/json struct reader would read the fabricated value (case-insensitive match, last wins). Fix before merge: refuse a run.result or run.audit member whose name equals disposition under case folding but is not exactly disposition; add the case to TestVerifyRunBindsASignedDisposition.
2 LOW The CLI's disposition-differs report is no longer reached by a test. Fix on this PR: change run.result and run.audit together in the version-2 export and expect the report.
3 LOW Refusal reports still say "retainedDisposition":"not-checked" after the comparison ran. Fix on this PR.
4 LOW SECURITY.md still says a record whose disposition was changed after the run verifies. Fix on this PR.
5 LOW The #52 docs name the internal function and give no upgrade note (a profile accepted before may now refuse to start; such releases and exports no longer run or verify). Fix on this PR: describe the check and add the note.
6 LOW verifyAcquisition still decodes the trusted key as plain hex (safe today because both callers validate first). Fix on this PR: pass the checked key in.
7 LOW MAPPING-V2 lacks the mismatch report and exit code; both answers null still "differs"; the coverage test checks the sentence, not the export structure. Fix on this PR.

Checked and holding: the key check runs on every path a trusted key enters, before any signature check; the comparison reads run.audit held to the signed bytes and runs before any other check or success output, under every flag set tried; the list of unsigned members matches the export structure.

One round: the fixes land on this PR and it merges on green with no second round.

…isposition sibling; test the CLI's disposition-differs report; truthful retainedDisposition; SECURITY.md and MAPPING-V2 corrected with an upgrade note; the parsed key passed into receipt verification

Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@kikashy

kikashy commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Round 1 fixes applied on head 74ccb19 (one commit; drafted with the OpenAI model that drafted the change, verified by the coordinator under the machine lock: vet, go test ./... with sockets, the race detector on the runner package): a case-folded disposition sibling in run.result or run.audit is refused with a named finding, with signed and witnessed regressions; the replay test changes result and audit together and expects disposition-differs; refusal reports say matches-audit once the comparison passed; SECURITY.md states the audit binding; the key check is described in plain words with an upgrade note (in MAPPING-V2 and a release-notes draft); the parsed key is passed into receipt verification; the mismatch report and exit code are documented, both-null is a clear refusal, and the coverage test follows the export's JSON tags. One round: merging on green, no second round.

@kikashy
kikashy merged commit 63ac14c into main Oct 9, 2026
3 checks passed
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.

1 participant