Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

keksbruch

Bruch is German for fracture — keksbruch bends cookies until they break.

A differential test harness for cookie wire. It runs a growing body of behavioural tests that surface divergence and drift across cookie implementations, so kekse stays correct, compliant, and robust even on bad input.

Where kekse emits only honest, canonical cookies, keksbruch exercises the hard cases — unbalanced quotes, spliced control bytes, truncated percent-escapes, smuggled ;, garbage Set-Cookie attributes, and the malformed shapes commonly seen in injection attempts, because that is exactly where implementations diverge most. A KeksbruchRecipe renders the same logical cookie two ways: a clean baseline() through kekse, and a malformed render() built directly as bytes (kekse's encoders refuse to emit injection bytes, so keksbruch constructs that wire by hand) — then checks how each parser copes.

Two layers

  • Layer A (tests/keksbruch_layer_a.rs, runs in CI) pins kekse's own behaviour against the curated scenarios() corpus. Every Keksbruch is checked against the universal invariants — never panics, never echoes an injection byte (;/CR/LF/NUL), strict ⊆ lenient — plus a per-scenario Expect. Pure Rust, no external dependencies.

    cargo test -p keksbruch
    
  • The differential matrix (opt-in, behind a differential feature, never in the gating CI) feeds the same payloads to cookie parsers across languages — Rust (cookie, cookie_store, biscotti, axum-extra), Python (stdlib SimpleCookie and http.cookiejar, Werkzeug, mitmproxy), Node (cookie, tough-cookie, set-cookie-parser, universal-cookie, js-cookie), Go (net/http), C (libcurl's cookie engine), real curl/wget clients over a loopback transfer, .NET (Microsoft.Net.Http.Headers), PHP (native $_COOKIE), nginx (openresty — native $cookie_<name>, lua-resty-cookie, and proxy forwarding fidelity), Java (Tomcat's Rfc6265CookieProcessor + LegacyCookieProcessor, and the jakarta.ws.rs cookie API via both RESTEasy and Jersey), and three real browser engines — Chromium, Firefox, and Edge, driven headless against a loopback origin so each engine's full RFC 6265bis pipeline (size caps, cookie prefixes, the public-suffix list, SameSite defaults) answers, not a re-implementation — and tabulates where they diverge, to see whether kekse is standard-compliant (RFC 6265) and expectation-compliant (what real parsers do). A third table of jar probes exercises client stores end-to-end — store a Set-Cookie from an origin URL, then ask which cookies attach to a request URL — against a reference built from rfc_6265's own primitives: the §5.1.3/§5.1.4 domain- and path-matching axis a single wire cannot probe. It writes COOKIE_MATRIX.md (one row per tool, one column per test), COOKIE_MATRIX.csv, a machine-readable COOKIE_MATRIX.json (the source of truth), and a self-contained COOKIE_MATRIX.html report — every untrusted cell entity-encoded, so a corrupted cookie's markup renders as text and never as live HTML; a sidecar whose toolchain is absent degrades to SKIP.

    cargo test -p keksbruch --features differential -- --ignored --nocapture
    

    The matrix carries kekse's issue channel end to end: an accepted-with-issues cell wears a display-only ⚠️ with the recovered list in its tooltip (issues never affect the outcome or the consensus vote), the attribute-fidelity grid is tri-state (// — kept, dropped, or not observable through the driver's protocol), and a Calibration section grades kekse's two dials against the answering columns on every run: a wire strict parses clean must be majority-accepted, a majority-accepted wire must be lenient-accepted, and every deliberate deviation is listed with its documented reason — a violation fails the run.

    The Differential matrix GitHub Action runs this in a clean environment with every toolchain installed (so no column is SKIP), uploads the four files as build artifacts, and (on main) publishes them to GitHub Pages — the HTML report (COOKIE_MATRIX.html) is the readable view, the .md/.csv sit beside it as downloads. The deploy job reports the live URL. Each sidecar speaks a small base64-JSONL protocol — see fixtures/PROTOCOL.md for the contract and how to add one.

Why

kekse is, by design, the paranoid outlier: fail-soft (a malformed pair is skipped, never aborting the header — optionally with a report of what was skipped), strict cookie-octet enforcement, case-sensitive names. The corpus makes those choices visible and deliberate — and a permanent regression oracle against accidental drift.

What keksbruch is (and is not)

keksbruch is a test and research harness — a quality-assurance tool, not a production library and not a cookie parser. Its job is to verify how parsers handle difficult input, so do not depend on it at runtime. The library under test is kekse; depend on that instead.

Known limitations

  • keksbruch generates malformed and edge-case cookie wire (injection-style bytes, smuggled ;, control characters, truncated escapes) purely as test input, to probe where parsers diverge. It is a measurement tool, not a certification.
  • The differential matrix reports observed divergence between parsers — an insight and comparison aid, not a conformance certification of any parser, including kekse.
  • The corpus is not claimed to be exhaustive; it grows with every test added, and passing Layer A does not prove a parser free of defects.

License

Licensed under the MIT License.

Third-party fixtures

fixtures/ vendors third-party parser source for the differential matrix (e.g. CloudFlare's BSD-licensed lua-resty-cookie) and drives others through their own build-time dependencies (Go, C, Node, PHP, Python, .NET, Java) or as pinned upstream binaries in a CI-built image (Chromium via Chrome for Testing, Firefox, Edge, and their WebDrivers — see fixtures/browsers/Dockerfile). Vendored files remain under their respective upstream licenses; see NOTICE and each file's header.