Repository navigation
SEL-297: TypeScript offline verifier rejects genuine receipts whose signed payload contains floats or large integers - #126
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: afdbc259f0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
0322d3a to
d268c43
Compare
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 4f1cf2af-4070-4511-8d9a-6942351583e3) |
b186f31 to
ed12f2c
Compare
…igned payload contains floats or large integers Linear: SEL-297 Refs #95 Engine: muse (fleet coding engine, Grok Bot dispatch)
ed12f2c to
f0422eb
Compare
Linear: SEL-297
Fixes #95
Coding engine: muse, dispatched by Grok Bot for C.Lee.
SEL-297 , Offline verifiers no longer recompute
payload_hashWhat changed
Genuine receipts whose signed payload contains JSON numbers (floats like
0.0/1.0fromjev_risk_guard, exponents like4e-05, integers above2^53) verified in Python but were rejected as
mismatchby the TypeScriptoffline verifier, because each verifier re-serialized the parsed payload to
recheck
payload_hashand the two languages render numbers differently.The Ed25519 signature already covers
signing_inputverbatim(
payload_hashincluded), so the recheck added no tamper protection , anyedit to the signed bytes still fails the signature as
invalid.sdk/typescript/src/verify.tsandsdk/python/src/b2a_sdk/receipt_verifier.py:removed the
payload_hashrecomputation. A signedpayload_hash(even awrong one) is now advisory; editing it without re-signing is
invalid.Also fixed the false
canonical_jsondocstring claim that numbers insigned payloads are already strings, and dropped the now-unused
hashlib/createHashimports.spec/README.md,spec/receipt-v0.1.md,spec/test-vectors/README.md:documented that verifiers never recompute
payload_hash.spec/test-vectors/generate.py+ regeneratedspec/test-vectors/receipts.json(52 cases):
synthetic.payload_hash_mismatchnow expectsverified(signed lie, signature holds), and a new
synthetic.float_payload_verifiescase carries
0.0,1.0,4e-05, and9007199254740993inconstraints_evaluated.jev_risk_guardand expectsverified.spec/test-vectors/test_contract.py,spec/test-vectors/test_generate.py:updated expectations; added assertions that the float vector really
contains the hostile renderings.
sdk/typescript/test/verify.test.ts,sdk/python/tests/test_receipt_verifier_signed.py: replaced thewrong-hash-is-
mismatchtests with signed-wrong-hash-verifies,edit-without-resign-is-
invalid, and float-bearing-payload-verifies tests.No gateway code changed: server-side verification rebuilds and checks
signatures in one language, so it never had this bug.
How verified (exact commands and results)
Reproduced first at current tip with a Python-signed bundle containing
{"latency_ms":0.0,"injected_instructions":1.0}: Python verifierverified, TS verifiermismatch "payload_hash does not match the signed payload". After the fix both reportverifiedon the identical bytes.cd sdk/typescript && node --test test/*.test.ts→ 110 tests,109 pass, 0 fail, 1 skipped (pre-existing unrelated crypto-backend skip).
npm run typecheck(tsc --noEmit) → clean;npm run build→ clean.PYTHONPATH=sdk/python/src python3 -m pytest sdk/python/tests/test_receipt_verifier_signed.py spec/test-vectors/test_contract.py spec/test-vectors/test_generate.py -q→ 57 passed.
PYTHONPATH=sdk/python/src python3 verifier/check_vectors.py→52/52 vectors passed.
sdk/python,pytest tests -q) → 215 passed.ruff checkon all changed Python files → clean;ruff format --checkclean for SDK files (the two spec test files were already unformatted
before this change; only my added lines were kept format-clean).
Not done / follow-ups
on a pre-existing, unrelated import error (
streamable_http_clientmissing from the installed
mcppackage, viagateway/app/services/upstream_mcp.py). Nothing undergateway/waschanged, and its verification path is unaffected.
with a canonicalization version bump (the issue's "better" option). That
is a breaking format change needing dual-version verifier support and
does not fix already-issued float receipts, so it belongs in a separate
decision, not this fix.
mcppackage version mismatch above may also affect CI environmentsetup and is worth a look independent of this issue.
Note
Medium Risk
Changes cross-language verifier semantics and spec contract for
payload_hash(fewermismatchoutcomes); tamper detection still depends on signature over verbatimsigning_input.Overview
Offline receipt verifiers no longer recompute
payload_hashafter parsingsigning_input. Verification relies on Ed25519 over the signed bytes only; a signedpayload_hash(even if wrong) is advisory and still yieldsverified, while changing it without re-signing staysinvalid.This fixes false
mismatchrejections when genuine gateway receipts include JSON numbers (Python-style floats like1.0/0.0, exponents, integers above 2^53) that do not round-trip through re-serialization. The TypeScript verifier addsparseJsonPreservingUnsafeIntegersso large integers inclaimsmatch the signed decimal text.Spec and vectors are updated:
payload_hashbehavior is documented inspec/README.mdandspec/receipt-v0.1.md;synthetic.payload_hash_mismatchnow expectsverified; newsynthetic.float_payload_verifiescase. Python verifier drops the hash check and clarifiescanonical_jsonis for signing new payloads, not verification.Reviewed by Cursor Bugbot for commit f0422eb. Bugbot is set up for automated code reviews on this repo. Configure here.