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
- 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]>
107 lines
4.6 KiB
Python
Executable file
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:]))
|