open-standards/wai/cache-conformance/mutators.py
Transaction Science 7f6b9f3cec
Some checks failed
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 14s
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 13s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Has been cancelled
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 2m3s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Successful in 2m32s
wai-quantum energy-landscape jobs / the same bits · macos (push) Successful in 4m40s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Successful in 5m15s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 6m25s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Successful in 7m57s
wai-audit collector and player integration / does this change touch it (push) Successful in 6m40s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 6m44s
WAI operation catalogue / does this change touch it (push) Successful in 6m42s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 6m45s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 6m45s
wai-app and Create / wai-app · does this change touch it (push) Successful in 6m45s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Has been cancelled
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Has been cancelled
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Has been cancelled
wai-audit collector and player integration / collector · linux (push) Has been cancelled
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Has been cancelled
WAI operation catalogue / the catalogue and every door · linux (push) Has been cancelled
wai-fleet service / service · linux (push) Has been cancelled
wai-fleet service / service · macos (push) Has been cancelled
wai-key-proxy / proxy · linux (push) Has been cancelled
wai-fleet service / does this change touch it (push) Has been cancelled
wai-ledger service / service · linux (push) Has been cancelled
wai-ledger service / service · macos (push) Has been cancelled
wai-key-proxy / does this change touch it (push) Has been cancelled
wai-quantum for JavaScript / javascript · linux (push) Has been cancelled
wai-quantum for JavaScript / does this change touch it (push) Has been cancelled
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Has been cancelled
wai-quantum studio and desktop / studio · browser (push) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (push) Has been cancelled
wai-quantum studio and desktop / desktop app · linux (push) Has been cancelled
wai-quantum studio and desktop / desktop app · windows (push) Has been cancelled
wai-ledger service / does this change touch it (push) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Has been cancelled
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Has been cancelled
WAI Studio / Studio in Chromium and Firefox · macos (push) Has been cancelled
WAI Studio / Studio in WebKit · linux (push) Has been cancelled
WAI Studio / does this change touch it (push) Has been cancelled
wai-sink-swift native sink package / does this change touch it (push) Has been cancelled
wai-quantum studio and desktop / does this change touch it (push) Has been cancelled
wai-sink-android native sink library / does this change touch it (push) Has been cancelled
wai-ops integer operators / does this change touch it (push) Successful in 6m21s
wai-ops integer operators / macos-arm64 (push) Successful in 1m3s
wai-ops integer operators / linux-x86_64 (push) Successful in 59s
wai-ops integer operators / independent checker (Python) (push) Successful in 18s
wai-ops integer operators / wasm32 under Node (push) Successful in 1m50s
wai-ops integer operators / wasm32-wasip2 (wasmtime) (push) Successful in 1m54s
wai-ops integer operators / linux-aarch64 (push) Successful in 1m59s
wai conform runs the browser sink, a learned decoder and a cache, and a case is required unless its corpus marks it informative
- sink browser: each call of live-conformance/browser given with its answer withheld, the
  answer held byte for byte; wai_browser_sink_vectors answers is the reference sink.
- decoder neural: each vector of neural-conformance, its parameter set as {model} and its
  payload as {in}, the RGB8 held to sha256.json; wai_int_synth_vectors decode is the reference.
- cache cache: the cache corpus's driver against a cache the command starts in front of the
  reference origin; mutators.py --mode relay is the positive control through a proxy.
- conform-result/1 cases may carry "level": "informative"; the verdict is over the required
  cases (conformance-results §2.3), in wai-rs and conform_result_verify.py, with eight vectors.
  --level all counts every case, for a suite whose corpus marks some informative.
- Admission rule 4 refuses a record that marks a required case informative, and
  wai verify --conformant gives each area its verdict.
- wai-breaking.yml signs and admits the three records and refuses a planted browser sink.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-08 09:04:31 -05:00

107 lines
4.6 KiB
Python
Executable file

#!/usr/bin/env python3
"""Negative controls for driver.py: proxies that break http-delivery §9 in one way each,
so a run against them must fail the tests that rule reaches. If the driver passes one of
them, the driver is what is broken.
python3 mutators.py --mode flip|gzip|truncate|vary|stored|ranged|reflip|relay --listen PORT --upstream URL
- flip: changes one byte in the middle of every object body (§9 rule 1);
- gzip: applies a gzip content coding to whole objects when the request offers it
(§9 rule 1);
- truncate: drops the last byte of every whole object, and says so in Content-Length
(§9 rules 1 and 3);
- vary: adds `Vary: Origin` and drops `Access-Control-Allow-Origin` (§9 rule 4,
§5.3);
- stored: answers the first whole GET of a path untouched, and any later one with
`no-transform` dropped from `Cache-Control`, no `ETag` and another
`Content-Type`, as a store could (§9 rule 3);
- ranged: does the same to every `206` (§9 rule 3);
- reflip: answers the first whole GET of a path untouched, and changes one byte in the
middle of any later one, as a store could (§9 rule 1);
- relay: changes nothing and stores nothing, as §9 rule 7 lets a cache relay what it does
not store: the positive control through a proxy, which must pass every MUST test.
Standard library only.
"""
import gzip as gz, http.client, sys, urllib.parse
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
MODE, UPSTREAM = None, None
SEEN = set()
HOP = {"connection", "keep-alive", "proxy-connection", "transfer-encoding", "upgrade", "te", "trailer", "host"}
def headers_changed(out):
kept = [(k, v) for k, v in out if k.lower() not in ("cache-control", "etag", "content-type")]
cc = next((v for k, v in out if k.lower() == "cache-control"), "")
return kept + [("Cache-Control", cc.replace(", no-transform", "")), ("Content-Type", "application/octet-stream")]
class Proxy(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def log_message(self, *a):
pass
def forward(self, method):
u = urllib.parse.urlsplit(UPSTREAM)
conn = http.client.HTTPConnection(u.hostname, u.port or 80, timeout=30)
headers = {k: v for k, v in self.headers.items() if k.lower() not in HOP}
conn.request(method, self.path, headers=headers)
r = conn.getresponse()
body = r.read()
out = [(k, v) for k, v in r.getheaders() if k.lower() not in HOP and k.lower() != "content-length"]
conn.close()
is_object = self.path.split("?")[0].endswith(".wai")
if MODE == "flip" and is_object and len(body) > 1:
body = bytearray(body)
body[len(body) // 2] ^= 0x01
body = bytes(body)
elif MODE == "gzip" and is_object and r.status == 200 and "gzip" in (self.headers.get("Accept-Encoding") or ""):
body = gz.compress(body, mtime=0)
out = [(k, v) for k, v in out if k.lower() != "content-encoding"] + [("Content-Encoding", "gzip")]
elif MODE == "truncate" and is_object and r.status == 200 and body:
body = body[:-1]
elif MODE == "vary":
out = [(k, v) for k, v in out if k.lower() != "access-control-allow-origin"] + [("Vary", "Origin")]
elif MODE == "stored" and self.path in SEEN and r.status == 200:
out = headers_changed(out)
elif MODE == "ranged" and r.status == 206:
out = headers_changed(out)
elif MODE == "reflip" and self.path in SEEN and r.status == 200 and len(body) > 1:
body = bytearray(body)
body[len(body) // 2] ^= 0x01
body = bytes(body)
SEEN.add(self.path)
self.send_response(r.status)
for k, v in out:
self.send_header(k, v)
self.send_header("Content-Length", str(len(body)))
self.send_header("Connection", "close")
self.end_headers()
if method != "HEAD":
self.wfile.write(body)
def do_GET(self):
self.forward("GET")
def do_HEAD(self):
self.forward("HEAD")
def do_OPTIONS(self):
self.forward("OPTIONS")
def main(argv):
global MODE, UPSTREAM
opt = lambda f: argv[argv.index(f) + 1]
MODE, UPSTREAM, port = opt("--mode"), opt("--upstream"), int(opt("--listen"))
if MODE not in ("flip", "gzip", "truncate", "vary", "stored", "ranged", "reflip", "relay"):
print(__doc__)
return 2
ThreadingHTTPServer(("127.0.0.1", port), Proxy).serve_forever()
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))