Files

114 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""probe_surface.py -- hunt for non-Json*Error escapes across every input surface.
For each generated (doc, patch) pair we call apply_patch and record whether the
exception is one of the documented contract errors or something else leaking.
Also exercises the CLI as a subprocess for a sample.
"""
import json
import os
import subprocess
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
ENGINE = os.path.join(HERE, "jsonptr_patch.py")
sys.path.insert(0, HERE)
import jsonptr_patch as E # noqa: E402
# Every weird token we can think of for a pointer segment.
WEIRD = [
"\u00b2", "\u00b3", "\u0661", "\u0662", "\u0660", "\U0001d7d9",
"\u00bd", "\u2153", "\uff11", "\u00b9", "\u2070", "\u06f1",
"1e0", "-0", "+0", "01", "0x1", " 1", "1 ", "0b1", "1_0",
"~", "~2", "~01", "~10", "~00", "~11", "", "-", "0", "00",
"\u0000", "\n", "\t", "a/b", "a~b", "%20", "\u200b",
]
DOCS = [
{},
[],
[1, 2, 3],
{"a": 1, "b": {"c": 2}},
{"\u00b2": "key-is-superscript"},
{"a": [10, 20]},
{"a": {"b": [{"c": 1}]}},
]
OPS = ["add", "remove", "replace", "move", "copy", "test"]
CONTRACT = (E.JsonPatchError, E.JsonPointerError)
def leaks(doc, patch):
"""Return None if the contract held, else (type, message)."""
try:
E.apply_patch(doc, patch)
except CONTRACT:
return None
except Exception as exc: # noqa: BLE001 - that is exactly what we hunt
return (type(exc).__name__, str(exc))
return None
def main():
found = []
n = 0
for doc in DOCS:
for tok in WEIRD:
ref = "/" + tok
for op in OPS:
patch = [{"op": op, "path": ref}]
if op in ("add", "replace", "test"):
patch[0]["value"] = 1
if op in ("move", "copy"):
patch[0]["from"] = ref
n += 1
leak = leaks(doc, patch)
if leak:
found.append((doc, ref, op, leak))
# two-token paths
for t1 in WEIRD:
for t2 in WEIRD:
ref = "/" + t1 + "/" + t2
patch = [{"op": "add", "path": ref, "value": 1}]
n += 1
leak = leaks(doc, patch)
if leak:
found.append((doc, ref, "add", leak))
print(f"apply_patch calls exercised: {n}")
print(f"contract violations (non-Json*Error escapes): {len(found)}")
seen = set()
for doc, ref, op, (tname, msg) in found:
key = (tname, msg)
if key in seen:
continue
seen.add(key)
print(f" {tname}: {msg} e.g. doc={doc!r} op={op} path={ref!r}")
# --- CLI surface on a sample of the leaks ---
print()
print("CLI surface (subprocess) on one violating input:")
if found:
_, ref, op, _ = found[0]
tmp = os.path.join(HERE, "_probe_cli_tmp")
os.makedirs(tmp, exist_ok=True)
doc_p = os.path.join(tmp, "d.json")
pat_p = os.path.join(tmp, "p.json")
json.dump(DOCS[0], open(doc_p, "w"))
payload = [{"op": op, "path": ref}]
if op in ("add", "replace", "test"):
payload[0]["value"] = 1
if op in ("move", "copy"):
payload[0]["from"] = ref
json.dump(payload, open(pat_p, "w"), ensure_ascii=False)
proc = subprocess.run([sys.executable, ENGINE, "--doc", doc_p,
"--patch", pat_p], capture_output=True, text=True)
print(f" rc={proc.returncode} traceback={'Traceback' in proc.stderr}")
print(f" stderr head: {' '.join(proc.stderr.split())[:160]}")
return 0
if __name__ == "__main__":
sys.exit(main())