Skip to content

Fix cross-runtime fingerprint canonicalization#135

Merged
luisleo526 merged 1 commit into
mainfrom
codex/fingerprint-json-canonical-20260723
Jul 23, 2026
Merged

Fix cross-runtime fingerprint canonicalization#135
luisleo526 merged 1 commit into
mainfrom
codex/fingerprint-json-canonical-20260723

Conversation

@luisleo526

Copy link
Copy Markdown
Collaborator

Summary

Fixes a cross-runtime fingerprint integrity mismatch where Python canonical JSON
encoded integral floats such as 12345.0, while JavaScript parsed and re-emitted
them as 12345. The provenance was semantically identical, but recomputing the
digest from the public MCP response could disagree with the engine-produced
digest.

  • replace Python json.dumps(sort_keys=True) fingerprint bytes with a direct
    RFC 8785/JCS-style canonical writer
  • make decoded token bytes the authoritative digest input
  • use ECMAScript number spellings, UTF-16 object-key ordering, raw valid
    Unicode, and fail-closed invalid inputs
  • apply the same byte-identical helper in local and Docker harnesses
  • document the verifier contract and strict safe-integer policy
  • add adversarial cross-runtime, numeric, Unicode, subclass, duplicate-key, and
    Node bridge regressions

This is generic fingerprint infrastructure only; it adds no strategy-specific
behavior.

Compatibility

The {token,digest,provenance} shape and semantic provenance values are
unchanged. Canonical token bytes and therefore digests rotate under the
corrected contract. Exact Python integers outside [-(2^53-1), 2^53-1],
non-finite numbers, unpaired surrogates, and normalized duplicate object keys
fail closed; existing callers return no fingerprint for such provenance.

Verification

  • scripts/fingerprint_self_test.py: 273/273 passed
  • independent Node bridge and official RFC 8785 representative vectors passed
  • 10,000,000 random IEEE-754 bit-pattern oracle comparison passed (9,995,256
    finite values)
  • all 1,112,064 valid Unicode scalar string oracle comparisons passed
  • Python 3.9 and 3.13 compatibility runs passed
  • NTU2 Release ctest -j 48: 120/120 passed
  • duplicate helper blocks are byte-identical
  • git diff --check and py_compile passed

Copilot AI review requested due to automatic review settings July 23, 2026 12:31
return "99"


class _WeirdFloat(float):
try:
m._canonical_fingerprint_json({"x": bad})
nonfinite_rejected = False
except ValueError:
try:
m._canonical_fingerprint_json(bad)
unsafe_int_rejected = False
except ValueError:
try:
m._canonical_fingerprint_json({"n": bad})
unsafe_int_rejected = False
except ValueError:
try:
m._canonical_fingerprint_json(_WeirdFloat(bad))
weird_float_nonfinite_rejected = False
except ValueError:
try:
m._canonical_fingerprint_json({_WeirdStr("\ud800"): "x"})
weird_str_surrogate_rejected = False
except ValueError:
try:
m._canonical_fingerprint_json(bad)
surrogate_rejected = False
except ValueError:
try:
m._canonical_fingerprint_json({bad: "x"})
surrogate_rejected = False
except ValueError:
try:
m._canonical_fingerprint_json({"k": bad})
surrogate_rejected = False
except ValueError:

Copilot AI 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.

Pull request overview

This PR fixes cross-runtime fingerprint verification drift by switching the Python harnesses from json.dumps(sort_keys=True) to a direct RFC 8785 / JCS-style canonical JSON writer, and by defining the decoded canonical token bytes as the authoritative digest input for verifiers.

Changes:

  • Implement a JCS-style canonical JSON encoder for fingerprint token/digest generation (including ES NumberToString numeric form and UTF-16 key ordering).
  • Update fingerprint self-tests with cross-runtime adversarial vectors (numeric edge cases, Unicode, duplicate-key normalization collisions, and optional Node bridge verification).
  • Expand Docker documentation to clarify the verifier contract (hash decoded token bytes; re-canonicalization requires an RFC 8785/JCS direct writer + IEEE-754 model).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
scripts/run_strategy.py Replaces legacy json.dumps(sort_keys=True) fingerprinting with a JCS-style canonical writer and hashes the resulting UTF-8 token bytes.
docker/run_json.py Mirrors the same canonical fingerprint implementation and updates the fingerprint contract comments.
scripts/fingerprint_self_test.py Adds extensive deterministic and adversarial regression coverage, plus an optional Node bridge check when node is available.
docker/README.md Documents the canonical fingerprint JSON rules and the “hash decoded token bytes” verifier contract.
Comments suppressed due to low confidence (2)

scripts/run_strategy.py:497

  • _canonical_fingerprint_json uses value.keys() and value[orig] for dict handling, which can invoke overridden keys() / __getitem__ on dict subclasses and change canonicalization output. Since the helper explicitly bypasses hostile subclass hooks for int/float/str, consider similarly forcing the base dict slots for key iteration and value lookup to keep canonical token bytes/digest stable under adversarial dict subclasses.
        for k in value.keys():
            if not isinstance(k, str):
                raise TypeError(
                    "fingerprint JSON object keys must be str, "
                    f"got {type(k).__name__}")

docker/run_json.py:509

  • _canonical_fingerprint_json uses value.keys() and value[orig] for dict handling, which can invoke overridden keys() / __getitem__ on dict subclasses and change canonicalization output. Since the helper explicitly bypasses hostile subclass hooks for int/float/str, consider similarly forcing the base dict slots for key iteration and value lookup to keep canonical token bytes/digest stable under adversarial dict subclasses.
        for k in value.keys():
            if not isinstance(k, str):
                raise TypeError(
                    "fingerprint JSON object keys must be str, "
                    f"got {type(k).__name__}")

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/run_strategy.py
Comment on lines +482 to +484
if isinstance(value, list):
return "[" + ",".join(
_canonical_fingerprint_json(v) for v in value) + "]"
Comment thread docker/run_json.py
Comment on lines +494 to +496
if isinstance(value, list):
return "[" + ",".join(
_canonical_fingerprint_json(v) for v in value) + "]"
@luisleo526
luisleo526 merged commit 8ceb716 into main Jul 23, 2026
8 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.

2 participants