From 90d333ed688f71a98f2c4cafe576dcc3052996e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Thu, 15 Oct 2026 00:39:15 -0700 Subject: [PATCH] Verify full finite JavaScript numbers in Python evidence --- docs/EVIDENCE_CAPSULE.md | 4 +-- independent/python/verify_evidence_archive.py | 4 +-- independent/python/verify_evidence_capsule.py | 31 +++++++++++++----- independent/python/verify_http_exchange.py | 4 +-- pilots/enterprise-http-gate/README.md | 11 +++++-- pilots/enterprise-http-gate/capture.mjs | 7 +++- scripts/check-evidence-archive-python.mjs | 3 +- scripts/check-evidence-capsule-python.mjs | 32 +++++++++++++++---- scripts/check-evidence-migration-python.mjs | 3 +- scripts/check-http-exchange-python.mjs | 8 +++-- 10 files changed, 79 insertions(+), 28 deletions(-) diff --git a/docs/EVIDENCE_CAPSULE.md b/docs/EVIDENCE_CAPSULE.md index fb7168a3..f772035c 100644 --- a/docs/EVIDENCE_CAPSULE.md +++ b/docs/EVIDENCE_CAPSULE.md @@ -22,7 +22,7 @@ python3 independent/python/verify_evidence_capsule.py capsule.json python3 independent/python/verify_evidence_capsule.py signed-capsule.json trusted-policy.json ``` -Unsigned capsules need one argument. Signed v1.1 capsules require a second JSON file with `trusted_receipt_fingerprints` (an array of SHA-256 SPKI DER fingerprints) and optional `revoked_receipt_fingerprints`. The embedded key is not a trust source. Legacy v1.0 signatures are unsupported in Python because their signer and signed time were not bound. The two implementations and their mutation cases are exercised with `node scripts/check-evidence-capsule-python.mjs` on CI, which publishes a JSON result artifact. Signed receipts with small exponents, fractions, negative zero, the minimum subnormal, the maximum safe integer, array-index-like keys, UTF-16-ordered keys, and escaped lone surrogates are checked there; mutation, escaped duplicate-key, and unsafe-integer cases must fail. This is cross-runtime engineering evidence maintained by the project, not an independent external witness. +Unsigned capsules need one argument. Signed v1.1 capsules require a second JSON file with `trusted_receipt_fingerprints` (an array of SHA-256 SPKI DER fingerprints) and optional `revoked_receipt_fingerprints`. The embedded key is not a trust source. Legacy v1.0 signatures are unsupported in Python because their signer and signed time were not bound. The two implementations and their mutation cases are exercised with `node scripts/check-evidence-capsule-python.mjs` on CI, which publishes a JSON result artifact. Signed receipts with small and large exponents, boundary integers, the maximum finite JavaScript number, array-index-like keys, UTF-16-ordered keys, and escaped lone surrogates are checked there; changed numerical values, escaped duplicate keys, and non-finite numbers must fail. This is cross-runtime engineering evidence maintained by the project, not an independent external witness. For a saved response from the locked HTTP deployment endpoint, the [HTTP exchange pilot](../pilots/enterprise-http-gate/README.md#recheck-a-real-http-exchange-with-python) additionally checks host-owned expected inputs, nonce, HTTP envelope and effective decision in Python while reusing this signed capsule verifier. The capsule alone does not make those request and response claims. @@ -30,7 +30,7 @@ For a saved response from the locked HTTP deployment endpoint, the [HTTP exchang The capsule has exactly six top-level keys: `protocol`, `canonicalization`, `claim_boundary`, `summary`, `receipt`, and `digests`. The digest material consists of the first five keys, serialized with `canonicalJSONStringify` from `protocol/canonicalJson.js`: ordinary JSON values, object keys sorted by JavaScript UTF-16 code-unit order before `JSON.stringify`, no whitespace, JSON string escaping, and JSON number serialization. JavaScript enumerates array-index keys numerically ahead of other keys during that final serialization. The receipt's internal hashes use a separate, hand-built sorted JSON rule that keeps those keys in UTF-16 lexical order. The Python verifier reproduces both byte orders without changing either frozen JavaScript hash. `undefined`, non-finite numbers, executable values, and non-plain objects are rejected. Hash the capsule payload's UTF-8 bytes with SHA-256 and SHA-512 as lowercase hexadecimal. -The receipt's own v1.1 verification remains a separate contract. Both digest algorithms are required in this capsule version. New algorithms and formats should get a new version and test vectors; do not reinterpret old bytes. Do not overwrite an old capsule when migrating: retain it and record a new, separately verifiable link to its exact digest. The [renewal chain](EVIDENCE_RENEWAL.md) implements one such link with a verifier-owned trust policy. No verifier here establishes an external timestamp or independent validation. The Python implementation handles tested finite fractions and exponent notation up to JavaScript's maximum safe-integer magnitude, rendering the shortest round-trip digits at JavaScript's fixed/exponent boundary. It reproduces the tested UTF-16 key order, array-index enumeration, and JSON escaping of lone surrogates. It rejects integers outside the safe range and non-finite numbers. This is a tested interoperability subset, not a proof of parity for every JavaScript number or arbitrary source string. Supplied JSON with duplicate keys is rejected before verification. +The receipt's own v1.1 verification remains a separate contract. Both digest algorithms are required in this capsule version. New algorithms and formats should get a new version and test vectors; do not reinterpret old bytes. Do not overwrite an old capsule when migrating: retain it and record a new, separately verifiable link to its exact digest. The [renewal chain](EVIDENCE_RENEWAL.md) implements one such link with a verifier-owned trust policy. No verifier here establishes an external timestamp or independent validation. The Python implementation handles tested finite fractions, large integers and exponents through JavaScript's maximum finite number, rendering shortest round-trip digits at JavaScript's fixed/exponent boundary. JSON integer tokens above the safe-integer limit are rounded to binary64 before verification, as in JavaScript: distinct decimal spellings can denote the same signed Number. Represent precision-critical identifiers and amounts as strings with a separate validation rule. Non-finite numbers are rejected. The verifier also reproduces tested UTF-16 key order, array-index enumeration, and JSON escaping of lone surrogates. This is a tested interoperability subset, not a proof of parity for every JavaScript number or arbitrary source string. Supplied JSON with duplicate keys is rejected before verification. ## Why preserve both views? diff --git a/independent/python/verify_evidence_archive.py b/independent/python/verify_evidence_archive.py index 4d73f703..0442e0b2 100644 --- a/independent/python/verify_evidence_archive.py +++ b/independent/python/verify_evidence_archive.py @@ -17,7 +17,7 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey -from verify_evidence_capsule import canonical, reject_constant, unique_pairs, verify as verify_capsule +from verify_evidence_capsule import canonical, parse_js_int, reject_constant, unique_pairs, verify as verify_capsule PROTOCOL = "aml-evidence-archive/1" ENCODING = "canonical-json-utf8-base64/1" @@ -42,7 +42,7 @@ def sha3(data): def strict_json(data): - return json.loads(data, object_pairs_hook=unique_pairs, parse_constant=reject_constant) + return json.loads(data, object_pairs_hook=unique_pairs, parse_constant=reject_constant, parse_int=parse_js_int) def component(archive, name, digest_name): diff --git a/independent/python/verify_evidence_capsule.py b/independent/python/verify_evidence_capsule.py index f9615576..a1c60603 100644 --- a/independent/python/verify_evidence_capsule.py +++ b/independent/python/verify_evidence_capsule.py @@ -39,15 +39,30 @@ def reject_constant(value): raise ValueError(f"unsupported JSON constant: {value}") +def parse_js_int(raw): + # JSON.parse represents every JSON number as binary64. Preserve small integers + # for Python structural checks; round larger decimal tokens to the same value. + number = int(raw) + if abs(number) <= MAX_SAFE_INTEGER: + return number + rounded = float(raw) + if not math.isfinite(rounded): + raise ValueError("unsupported non-finite JSON number") + return rounded + + def number_text(value): - # Match JSON.stringify's fixed/exponent boundary for the supported numeric subset. + # Match JSON.stringify's fixed/exponent boundary for finite binary64 numbers. # repr(float) supplies the shortest round-trip digits; Decimal only places the point. if type(value) is int: - if abs(value) > MAX_SAFE_INTEGER: - raise ValueError("unsupported JSON integer outside JavaScript safe range") - return str(value) - if not math.isfinite(value) or abs(value) > MAX_SAFE_INTEGER: - raise ValueError("unsupported JSON number outside finite safe-magnitude subset") + if abs(value) <= MAX_SAFE_INTEGER: + return str(value) + try: + value = float(value) + except OverflowError as error: + raise ValueError("unsupported non-finite JSON number") from error + if not math.isfinite(value): + raise ValueError("unsupported non-finite JSON number") if value == 0: return "0" sign = "-" if value < 0 else "" @@ -238,9 +253,9 @@ def verify(capsule, policy=None): if len(sys.argv) not in (2, 3): raise ValueError("Usage: python3 independent/python/verify_evidence_capsule.py [trusted-policy.json]") capsule = json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"), - object_pairs_hook=unique_pairs, parse_constant=reject_constant) + object_pairs_hook=unique_pairs, parse_constant=reject_constant, parse_int=parse_js_int) policy = json.loads(pathlib.Path(sys.argv[2]).read_text(encoding="utf-8"), - object_pairs_hook=unique_pairs, parse_constant=reject_constant) if len(sys.argv) == 3 else None + object_pairs_hook=unique_pairs, parse_constant=reject_constant, parse_int=parse_js_int) if len(sys.argv) == 3 else None report = verify(capsule, policy) except (ValueError, KeyError, TypeError, OSError, OverflowError, RecursionError) as error: report = {"protocol": "aml-evidence-capsule-python-report/1", "verified": False, "reason": str(error)} diff --git a/independent/python/verify_http_exchange.py b/independent/python/verify_http_exchange.py index 27894725..c488d6f3 100644 --- a/independent/python/verify_http_exchange.py +++ b/independent/python/verify_http_exchange.py @@ -9,7 +9,7 @@ import re import sys -from verify_evidence_capsule import canonical, unique_pairs, reject_constant, verify +from verify_evidence_capsule import canonical, unique_pairs, reject_constant, parse_js_int, verify MAX_FILE_BYTES = 8 * 1024 * 1024 NONCE = re.compile(r"[0-9a-f]{64}\Z") @@ -25,7 +25,7 @@ def read_json(filename): if path.stat().st_size > MAX_FILE_BYTES: raise ValueError("input_too_large") return json.loads(path.read_text(encoding="utf-8"), - object_pairs_hook=unique_pairs, parse_constant=reject_constant) + object_pairs_hook=unique_pairs, parse_constant=reject_constant, parse_int=parse_js_int) def check_exchange(exchange, capsule, expected, policy): diff --git a/pilots/enterprise-http-gate/README.md b/pilots/enterprise-http-gate/README.md index 89d16589..318362ee 100644 --- a/pilots/enterprise-http-gate/README.md +++ b/pilots/enterprise-http-gate/README.md @@ -50,7 +50,7 @@ Run `node --test test/enterprise-http-gate.test.js` or inspect the `aml-signed-g ## Recheck a real HTTP exchange with Python -The project also supplies a separate Python verifier for saved, parsed responses from this locked HTTP service. Install Python 3.12 and `cryptography>=42,<51`, then capture the repository's synthetic allow, consent suppression, exponent-bearing intent, and unusual-key fixtures into a **new** directory: +The project also supplies a separate Python verifier for saved, parsed responses from this locked HTTP service. Install Python 3.12 and `cryptography>=42,<51`, then capture the repository's synthetic allow, consent suppression, small exponent, unusual-key, and large-number fixtures into a **new** directory: ```bash node pilots/enterprise-http-gate/capture.mjs --out /tmp/aml-http-capture-example @@ -74,8 +74,13 @@ python3 independent/python/verify_http_exchange.py \ /tmp/aml-http-capture-example/capsule-keys.json \ /tmp/aml-http-capture-example/host-expected-keys.json \ /tmp/aml-http-capture-example/trusted-policy.json +python3 independent/python/verify_http_exchange.py \ + /tmp/aml-http-capture-example/exchange-large.json \ + /tmp/aml-http-capture-example/capsule-large.json \ + /tmp/aml-http-capture-example/host-expected-large.json \ + /tmp/aml-http-capture-example/trusted-policy.json ``` -The first, third, and fourth reports have `verified: true`, `status: "allow"`; the second has `verified: true`, `status: "suppressed"`, and `render_eligible: false`. The third intent carries `1e-7` through a real signed HTTP response and the Python verifier. The fourth carries array-index and Unicode keys plus an escaped lone surrogate. The verifier compares each response receipt to its evidence capsule, checks the capsule's internal hashes, audit chain, ledger, Ed25519 signature and **separately supplied** fingerprint policy, then binds the signed nonce, intent, profile and context to the host's saved expectation. It checks the HTTP status, server-owned enforcement settings, and effective decision. It reports the signed output hash; it never executes the HTML or trusts the outer `selected_render.html` as output. It exits nonzero on rejected evidence. +The first, third, fourth, and fifth reports have `verified: true`, `status: "allow"`; the second has `verified: true`, `status: "suppressed"`, and `render_eligible: false`. The third intent carries `1e-7` through a real signed HTTP response and the Python verifier. The fourth carries array-index and Unicode keys plus an escaped lone surrogate. The fifth carries `2**53` and `1e100`. The verifier compares each response receipt to its evidence capsule, checks the capsule's internal hashes, audit chain, ledger, Ed25519 signature and **separately supplied** fingerprint policy, then binds the signed nonce, intent, profile and context to the host's saved expectation. It checks the HTTP status, server-owned enforcement settings, and effective decision. It reports the signed output hash; it never executes the HTML or trusts the outer `selected_render.html` as output. It exits nonzero on rejected evidence. -Run `node scripts/check-http-exchange-python.mjs` to generate fresh loopback exchanges and exercise 26 cross-runtime cases, including the numeric and unusual-key intents, altered host expectations, replay, foreign keys, changed receipt content, altered decision, changed context, revocation, and an outer HTML swap. Enterprise Readiness CI uploads the compact `aml-locked-http-python-` report. The capture creates its own ephemeral key and writes the corresponding fingerprint to a separate file for this local fixture; a real host must provision and review that trust pin independently. Only the project fixtures are used here. The full saved exchanges may contain declared intent and context, so review them before sharing. Neither this project-authored Python implementation nor its passing examples establish an external witness, truthful inputs, live network identity, browser paint, or production fitness. +Run `node scripts/check-http-exchange-python.mjs` to generate fresh loopback exchanges and exercise 28 cross-runtime cases, including small and large numeric and unusual-key intents, altered host expectations, replay, foreign keys, changed receipt content, altered decision, changed context, revocation, and an outer HTML swap. Enterprise Readiness CI uploads the compact `aml-locked-http-python-` report. The capture creates its own ephemeral key and writes the corresponding fingerprint to a separate file for this local fixture; a real host must provision and review that trust pin independently. Only the project fixtures are used here. The full saved exchanges may contain declared intent and context, so review them before sharing. Neither this project-authored Python implementation nor its passing examples establish an external witness, truthful inputs, live network identity, browser paint, or production fitness. diff --git a/pilots/enterprise-http-gate/capture.mjs b/pilots/enterprise-http-gate/capture.mjs index 10eb4d6e..6eec221c 100644 --- a/pilots/enterprise-http-gate/capture.mjs +++ b/pilots/enterprise-http-gate/capture.mjs @@ -33,10 +33,15 @@ try { const results = []; for (const [name, intentName, shouldAllow] of [ ["allow", "intent-allowed.json", true], ["suppress", "intent-sensitive.json", false], - ["numeric", "intent-allowed.json", true], ["keys", "intent-allowed.json", true] + ["numeric", "intent-allowed.json", true], ["keys", "intent-allowed.json", true], + ["large", "intent-allowed.json", true] ]) { const intent = fixture(intentName); if (name === "numeric") intent.numeric_probe = 1e-7; + if (name === "large") { + intent.numeric_probe = 2 ** 53; + intent.large_exponent_probe = 1e100; + } if (name === "keys") intent.key_probe = { "10": "ten", "2": "two", "\uE000": "private use", "\u{1F4A1}": "astral", lone: "\uD800" }; diff --git a/scripts/check-evidence-archive-python.mjs b/scripts/check-evidence-archive-python.mjs index c7c4f31a..3b55c789 100644 --- a/scripts/check-evidence-archive-python.mjs +++ b/scripts/check-evidence-archive-python.mjs @@ -108,6 +108,7 @@ try { results.push(check("signed-archive-key-revoked", signedArchive, { ...signedPolicy, revoked_receipt_fingerprints: signedPolicy.trusted_receipt_fingerprints }, false, "invalid_or_unsupported_capsule")); const keyedReceipt = signExecutionReceipt(executeAccountableIntent({ ...intent, + numeric_probe: 2 ** 53, large_exponent_probe: 1e100, key_probe: { "10": "ten", "2": "two", "\uE000": "private use", "\u{1F4A1}": "astral", lone: "\uD800" } }, { profile: "human_first", timestamp: "2026-10-10T00:00:00.000Z" }), keys[0], { signer: "synthetic-receipt", timestamp: "2026-10-10T00:30:00.000Z" }); @@ -117,7 +118,7 @@ try { const keyedPolicy = { threshold: 1, trusted_fingerprints: [keyedWitness.public_key_fingerprint_sha256], trusted_receipt_fingerprints: [keyedReceipt.signature.public_key_sha256] }; const keyedArchive = createEvidenceArchive(keyedCapsule, [{ record: keyedRecord, witnesses: [keyedWitness] }], keyedPolicy); - results.push(check("signed-archive-index-and-unicode-keys", keyedArchive, keyedPolicy, true)); + results.push(check("signed-archive-complex-json-values", keyedArchive, keyedPolicy, true)); process.stdout.write(`${JSON.stringify({ protocol: "aml-archive-cross-runtime-check/1", passed: true, results }, null, 2)}\n`); } finally { diff --git a/scripts/check-evidence-capsule-python.mjs b/scripts/check-evidence-capsule-python.mjs index 4dd3f15f..eadf6742 100644 --- a/scripts/check-evidence-capsule-python.mjs +++ b/scripts/check-evidence-capsule-python.mjs @@ -63,7 +63,11 @@ try { ["small-exponent", 1e-7], ["fixed-boundary", 1e-6], ["negative-fraction", -0.0000015], ["negative-zero", -0], ["minimum-subnormal", Number.MIN_VALUE], - ["maximum-safe-integer", Number.MAX_SAFE_INTEGER] + ["maximum-safe-integer", Number.MAX_SAFE_INTEGER], + ["first-beyond-safe", 2 ** 53], ["rounded-large-integer", 1000000000000000100], + ["large-fixed", 1e20], ["large-exponent-boundary", 1e21], + ["large-exponent", 1e100], ["maximum-finite", Number.MAX_VALUE], + ["negative-large", -1e250] ]) { const numericReceipt = executeAccountableIntent({ ...intent, numeric_probe: value }, { profile: "human_first", timestamp: "2026-10-10T00:00:00.000Z" }); @@ -77,13 +81,29 @@ try { rehash(tampered); results.push(check("signed-numeric-tampered", tampered, false, signedPolicy)); } + if (name === "first-beyond-safe") { + const tampered = structuredClone(numericCapsule); + tampered.receipt.intent.numeric_probe = 2 ** 53 + 2; + rehash(tampered); + results.push(check("signed-large-number-tampered", tampered, false, signedPolicy)); + const serialized = JSON.stringify(numericCapsule); + const alias = serialized.replace('"numeric_probe":9007199254740992', '"numeric_probe":9007199254740993'); + if (alias === serialized || JSON.parse(alias).receipt.intent.numeric_probe !== value) { + throw new Error("Numeric alias fixture does not preserve the JavaScript Number"); + } + results.push(check("signed-large-number-same-value-alias", alias, true, signedPolicy)); + const nonFinite = serialized.replace('"numeric_probe":9007199254740992', '"numeric_probe":1e400'); + results.push(check("signed-nonfinite-json-rejected", nonFinite, false, signedPolicy, + "unsupported non-finite JSON number")); + } } - const unsafeReceipt = executeAccountableIntent({ ...intent, numeric_probe: 2 ** 53 }, - { profile: "human_first", timestamp: "2026-10-10T00:00:00.000Z" }); - const unsafeSignature = signExecutionReceipt(unsafeReceipt, privateKey.export({ type: "pkcs8", format: "pem" }), + const largeBudgetContext = JSON.parse(fs.readFileSync("pilots/enterprise-30min/context-denied.json", "utf8")); + largeBudgetContext.attention_budget_remaining = 2 ** 53; + const largeBudgetReceipt = executeAccountableIntent(intent, + { profile: "human_first", context: largeBudgetContext, timestamp: "2026-10-10T00:00:00.000Z" }); + const largeBudgetSignature = signExecutionReceipt(largeBudgetReceipt, privateKey.export({ type: "pkcs8", format: "pem" }), { signer: "synthetic", timestamp: "2026-10-10T01:00:00.000Z" }); - results.push(check("signed-unsafe-integer-rejected", createEvidenceCapsule(unsafeSignature), false, signedPolicy, - "outside JavaScript safe range")); + results.push(check("signed-large-attention-budget", createEvidenceCapsule(largeBudgetSignature), true, signedPolicy)); for (const [name, key_probe] of [ ["array-index-keys", { "10": "ten", "2": "two", "01": "leading", "0": "zero", "4294967294": "last index", "4294967295": "ordinary key" }], diff --git a/scripts/check-evidence-migration-python.mjs b/scripts/check-evidence-migration-python.mjs index 661ee16b..d7ccd9b9 100644 --- a/scripts/check-evidence-migration-python.mjs +++ b/scripts/check-evidence-migration-python.mjs @@ -93,6 +93,7 @@ try { const intent = JSON.parse(fs.readFileSync("pilots/enterprise-30min/intent-allowed.json", "utf8")); const keyedReceipt = signExecutionReceipt(executeAccountableIntent({ ...intent, + numeric_probe: 2 ** 53, large_exponent_probe: 1e100, key_probe: { "10": "ten", "2": "two", "\uE000": "private use", "\u{1F4A1}": "astral", lone: "\uD800" } }, { profile: "human_first", timestamp: "2026-10-10T00:00:00.000Z" }), privateKey, { signer: "synthetic", timestamp: "2026-10-10T03:00:00.000Z" }); @@ -103,7 +104,7 @@ try { trusted_receipt_fingerprints: [keyedReceipt.signature.public_key_sha256] }; const keyedArchive = createEvidenceArchive(keyedCapsule, [{ record: keyedRecord, witnesses: [keyedWitness] }], keyedPolicy); const keyedMigration = createEvidenceMigration(keyedArchive, keyedPolicy); - const keyed = check("signed-index-and-unicode-keys", keyedMigration, keyedPolicy, true, null, "recover"); + const keyed = check("signed-complex-json-values", keyedMigration, keyedPolicy, true, null, "recover"); if (!fs.readFileSync(keyed.destination).equals(Buffer.from(`${canonicalJSONStringify(keyedArchive)}\n`))) { throw new Error("Python did not preserve the exact keyed v1 archive bytes"); } diff --git a/scripts/check-http-exchange-python.mjs b/scripts/check-http-exchange-python.mjs index b1a27234..e288e4d9 100644 --- a/scripts/check-http-exchange-python.mjs +++ b/scripts/check-http-exchange-python.mjs @@ -31,8 +31,8 @@ try { ["pilots/enterprise-http-gate/capture.mjs", "--out", captureDir], { encoding: "utf8" }); assert.equal(capture.status, 0, capture.stderr); const captured = JSON.parse(capture.stdout); - assert.equal(captured.cases.length, 4); - const original = Object.fromEntries(["allow", "suppress", "numeric", "keys"].map(name => [name, { + assert.equal(captured.cases.length, 5); + const original = Object.fromEntries(["allow", "suppress", "numeric", "keys", "large"].map(name => [name, { exchange: read(`exchange-${name}.json`), capsule: read(`capsule-${name}.json`), expected: read(`host-expected-${name}.json`) }])); @@ -69,6 +69,10 @@ try { check("keyed_host_expectation_changed", "keys", "blocked", "receipt_binding_mismatch", sample => { sample.expected.intent.key_probe["2"] = "altered"; }); + check("valid_large_finite_numbers", "large", "allow", "verified_allow"); + check("large_host_expectation_changed", "large", "blocked", "receipt_binding_mismatch", sample => { + sample.expected.intent.numeric_probe = 2 ** 53 + 2; + }); const swapped = check("outer_html_swap", "allow", "allow", "verified_allow", sample => { sample.exchange.body.selected_render.html = "

Unreviewed replacement

"; });