Skip to content

SEL-297: TypeScript offline verifier rejects genuine receipts whose signed payload contains floats or large integers - #126

Merged
PetrefiedThunder merged 3 commits into
mainfrom
agent/muse/sel-297
Oct 19, 2026
Merged

PetrefiedThunder merged 3 commits into
mainfrom
agent/muse/sel-297

Conversation

@PetrefiedThunder

@PetrefiedThunder PetrefiedThunder commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Linear: SEL-297
Fixes #95

Coding engine: muse, dispatched by Grok Bot for C.Lee.

SEL-297 , Offline verifiers no longer recompute payload_hash

What changed

Genuine receipts whose signed payload contains JSON numbers (floats like
0.0/1.0 from jev_risk_guard, exponents like 4e-05, integers above
2^53) verified in Python but were rejected as mismatch by the TypeScript
offline verifier, because each verifier re-serialized the parsed payload to
recheck payload_hash and the two languages render numbers differently.
The Ed25519 signature already covers signing_input verbatim
(payload_hash included), so the recheck added no tamper protection , any
edit to the signed bytes still fails the signature as invalid.

  • sdk/typescript/src/verify.ts and sdk/python/src/b2a_sdk/receipt_verifier.py:
    removed the payload_hash recomputation. A signed payload_hash (even a
    wrong one) is now advisory; editing it without re-signing is invalid.
    Also fixed the false canonical_json docstring claim that numbers in
    signed payloads are already strings, and dropped the now-unused
    hashlib/createHash imports.
  • 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 + regenerated spec/test-vectors/receipts.json
    (52 cases): synthetic.payload_hash_mismatch now expects verified
    (signed lie, signature holds), and a new synthetic.float_payload_verifies
    case carries 0.0, 1.0, 4e-05, and 9007199254740993 in
    constraints_evaluated.jev_risk_guard and expects verified.
  • 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 the
    wrong-hash-is-mismatch tests 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 verifier
verified, TS verifier mismatch "payload_hash does not match the signed payload". After the fix both report verified on 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.
  • Full Python SDK suite (sdk/python, pytest tests -q) → 215 passed.
  • ruff check on all changed Python files → clean; ruff format --check
    clean 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

  • Gateway test suites could not run in this environment: collection fails
    on a pre-existing, unrelated import error (streamable_http_client
    missing from the installed mcp package, via
    gateway/app/services/upstream_mcp.py). Nothing under gateway/ was
    changed, and its verification path is unaffected.
  • Deliberately not done: server-side normalization of numbers to strings
    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.
  • The mcp package version mismatch above may also affect CI environment
    setup and is worth a look independent of this issue.

Note

Medium Risk
Changes cross-language verifier semantics and spec contract for payload_hash (fewer mismatch outcomes); tamper detection still depends on signature over verbatim signing_input.

Overview
Offline receipt verifiers no longer recompute payload_hash after parsing signing_input. Verification relies on Ed25519 over the signed bytes only; a signed payload_hash (even if wrong) is advisory and still yields verified, while changing it without re-signing stays invalid.

This fixes false mismatch rejections when genuine gateway receipts include JSON numbers (Python-style floats like 1.0/0.0, exponents, integers above 2^53) that do not round-trip through re-serialization. The TypeScript verifier adds parseJsonPreservingUnsafeIntegers so large integers in claims match the signed decimal text.

Spec and vectors are updated: payload_hash behavior is documented in spec/README.md and spec/receipt-v0.1.md; synthetic.payload_hash_mismatch now expects verified; new synthetic.float_payload_verifies case. Python verifier drops the hash check and clarifies canonical_json is 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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-19T00:16:20.184629Z afdbc25 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread sdk/typescript/src/verify.ts Outdated
@cursor

cursor Bot commented Oct 19, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@PetrefiedThunder
PetrefiedThunder merged commit f435e5b into main Oct 19, 2026
43 of 44 checks passed
@PetrefiedThunder
PetrefiedThunder deleted the agent/muse/sel-297 branch October 19, 2026 10:17
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.

TypeScript offline verifier rejects genuine receipts whose signed payload contains floats or large integers

1 participant