utas: first real corpus, and two sanitiser gaps the audit caught
24 transactions across 11 connections from a retail session: login, hub, one pack open, two squad saves, a quick-sell, with before/after state manifests. Raw .ofcap stays gitignored at 0600; the sanitized corpus is committed as adapter fixtures. TWO GAPS FOUND BY AUDITING THE OUTPUT, NOT BY TRUSTING THE SANITISER. 1. `POST /ut/auth` carries `macAddress` and `deviceId`. Session tokens were being redacted correctly and these were not. A committed fixture is a published fixture. 2. Then, with those fixed, the audit fired AGAIN on the file about to be committed: `GET .../phishing/trusteddevice?deviceId=...` puts the id in the QUERY STRING. Three input surfaces carry identifiers -- headers, JSON bodies, and query strings -- and the sanitiser knew about two. Both fixed in the tool rather than by editing the file, with a regression test and a mutation for the query path. AND A THIRD ARTEFACT MIX-UP, in the mutation harness itself. It reported the query-redaction mutation as SURVIVED while a hand-run of the same mutation killed it. Cause: the harness pointed at a stale scratchpad copy of the test that pre-dated the query assertion, so it was faithfully testing the mutated tool against a test that could not detect the mutation. That is the same class as the build guard checking the wrong binary and cargo reusing a binary compiled from mutated source -- the third instance today of measuring the wrong artifact. The harness now resolves ROOT from its own location and runs the COMMITTED test; the stale copy is deleted. Harness committed as scripts/mutate-utas-observe.py so this is repeatable rather than a thing that happened once in a scratch directory. 6/6 killed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -50,7 +50,7 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
return self.rfile.read(n) if n else b""
|
||||
|
||||
def do_GET(self):
|
||||
if self.path == "/chunked":
|
||||
if self.path.split("?")[0] == "/chunked":
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Transfer-Encoding", "chunked")
|
||||
@@ -126,7 +126,7 @@ def main():
|
||||
b"GET /ut/game/fifa17/userMassInfo HTTP/1.1\r\nHost: t\r\n\r\n",
|
||||
b"POST /ut/game/fifa17/purchased/items HTTP/1.1\r\nHost: t\r\n"
|
||||
b"Content-Type: application/json\r\nContent-Length: %d\r\n\r\n" % len(body) + body,
|
||||
b"GET /chunked HTTP/1.1\r\nHost: t\r\n\r\n",
|
||||
b"GET /chunked?deviceId=DEADBEEFCAFE&keep=yes HTTP/1.1\r\nHost: t\r\n\r\n",
|
||||
b"GET /ut/game/fifa17/hub HTTP/1.1\r\nHost: t\r\nConnection: close\r\n\r\n",
|
||||
]
|
||||
|
||||
@@ -199,6 +199,16 @@ def main():
|
||||
check("secret JSON value redacted", echoed.get("sid") == "<REDACTED>")
|
||||
check("non-secret payload preserved exactly", echoed.get("echo_len") == len(body))
|
||||
check("redaction is recorded, not silent", "redacted" in txs[1])
|
||||
# Regression: the sanitiser handled headers and JSON bodies but not the
|
||||
# QUERY STRING, and was one audit away from publishing a device id from
|
||||
# `?deviceId=...`. Three surfaces carry identifiers, not two.
|
||||
qtx = next((t for t in txs if t["request"]["query"]), None)
|
||||
check("a secret query parameter is redacted",
|
||||
qtx is not None and "deviceId=<REDACTED>" in qtx["request"]["query"],
|
||||
qtx["request"]["query"] if qtx else "no query captured")
|
||||
check("a non-secret query parameter is preserved",
|
||||
qtx is not None and "keep=yes" in qtx["request"]["query"],
|
||||
qtx["request"]["query"] if qtx else "")
|
||||
|
||||
srv.shutdown()
|
||||
print()
|
||||
|
||||
Reference in New Issue
Block a user