114 lines
3.6 KiB
Python
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())
|