Repository navigation
The non-UTF-8 cookie error in the Edge Cookie path cannot be reached on any adapter #1098
Description
Found while reviewing the Edge Cookie provider work in
PR #1043. The
behavior predates that pull request.
The problem
handle_request_cookies returns an InvalidHeaderValue error when the request
Cookie header is not valid UTF-8, and the Edge Cookie lifecycle propagates
that error. On all four adapters, the GPT diagnostics request preparer rewrites
the Cookie header before the Edge Cookie context is ever built, keeping only
the field lines that decode as UTF-8, so by the time the Edge Cookie code runs
the header is always either valid UTF-8 or absent. The error arm is dead on
every adapter path that builds Edge Cookie state.
The same rewrite has a second consequence, which is that a request whose
Cookie header carries any byte outside UTF-8 silently loses every cookie in
that field line, including a perfectly good ts-ec value sitting next to the
bad bytes. Nothing logs the loss.
What was checked and found
Read on the PR #1043
branch. The line numbers below are from that branch tree.
crates/trusted-server-core/src/cookies.rs:51-67ishandle_request_cookies.
It callsHeaderValue::to_stron theCookieheader and maps the failure to
TrustedServerError::InvalidHeaderValuewith the message "Cookie header
contains invalid UTF-8".crates/trusted-server-core/src/ec/mod.rs:104is the Edge Cookie lifecycle's
call into it, so the error surfaces from Edge Cookie context construction.crates/trusted-server-core/src/integrations/gpt_diagnostics.rs:394-416is
sanitize_console_cookie. It reads everyCookiefield line through
to_str().ok(), dropping any line that is not UTF-8, removes theCookie
header outright, and reinserts only the retained lines.sanitize_console_cookieis called fromprepare_requestat
gpt_diagnostics.rs:277, before the enabled check that shapes the rest of the
decision, so the rewrite happens even in a deployment with GPT diagnostics
switched off.- Each adapter calls
gpt_diagnostics::prepare_requestbefore it builds Edge
Cookie state.- Fastly,
crates/trusted-server-adapter-fastly/src/app.rs:585, immediately
beforebuild_ec_request_stateat:592. - Axum,
crates/trusted-server-adapter-axum/src/app.rs:146in the shared
request wrapper and:207on the fallback path. - Cloudflare,
crates/trusted-server-adapter-cloudflare/src/app.rs:200. - Spin,
crates/trusted-server-adapter-spin/src/app.rs:577and:718, with
build_ec_contextfollowing at:590.
- Fastly,
- One caller can still meet a non-UTF-8
Cookieheader, being the Fastly admin
Edge Cookie lookup at
crates/trusted-server-adapter-fastly/src/app.rs:576, which is dispatched
beforeprepare_requestruns at:585. It has its own message at
crates/trusted-server-core/src/ec/admin.rs:327-333. That route exists on the
Fastly adapter alone, and the other three adapters answer the admin paths with
deny_admin_diagnostic_fallback.
Why it matters
Three things follow.
- An error path that cannot run is untested behavior that reads as tested. Any
future reader takes the Edge Cookie code as handling a hostileCookie
header when in fact something else already handled it. - The handling that does happen is silent cookie loss rather than a refusal, so
a returning visitor with one malformed byte in their cookie header looks like
a brand new visitor, and no log line says why. - The ordering is load bearing and undocumented. If the GPT diagnostics
preparer ever stops running first, or stops rewriting the header, the dead
arm comes alive on every adapter at once.
What would resolve it
Decide which layer owns malformed cookie headers, then make the code say so.
- If dropping the malformed field line is the wanted behavior, emit a bounded operational signal
fromsanitize_console_cookie, say so in the doc comment on
handle_request_cookies, and either delete the unreachable arm or keep it with
a comment naming the preparer that makes it unreachable. - If refusing the request is the wanted behavior, move the UTF-8 check ahead of
the preparer so the refusal actually happens.
Either way, add a test at the adapter level rather than the unit level, because
the unit tests for the error arm pass today while no adapter can reach it.
This issue does not depend on any pull request in the current stack, though the
line numbers above are from the
PR #1043 branch.
Note on authorship
This issue was drafted with AI assistance. Every file and line reference above
was read at the commit named. A human should review it before acting on it.
Done when
- A test through an adapter request path records what happens to a
Cookiefield line containing non-UTF-8 bytes before Edge Cookie state is built. - The chosen owner of malformed-cookie policy is explicit in code and documentation: either the preparer drops the field line observably, or a deliberate rejection happens before it. Any signal must avoid unbounded client-driven log volume.
- Tests cover a valid
ts-eccookie on a malformed field line and confirm the selected behavior on every adapter path that builds Edge Cookie context, or document a proven adapter difference. - The unreachable
handle_request_cookieserror arm is removed or documented with its actual call-path assumptions.
Activity
aram356 commentedon Sep 24, 2026
Two additions, checked on main at a4e01eb.
The trigger is wider than invalid UTF-8.
HeaderValue::to_strinhttp1.4.2 accepts only visible ASCII:Valid UTF-8 fails it as well. A field line such as
ts-ec=abc.Ab1234; city=Münchenis dropped whole bysanitize_console_cookie(crates/trusted-server-core/src/integrations/gpt_diagnostics.rs:399,.filter_map(|value| value.to_str().ok())), and the goodts-ecgoes with it. Without the preparer,handle_request_cookiesrejects the same header with "Cookie header contains invalid UTF-8" (crates/trusted-server-core/src/cookies.rs:57-62), which is not accurate for this input.It applies when GPT diagnostics is not configured at all, not only when it is switched off.
prepare_requestcallssanitize_console_cookieatgpt_diagnostics.rs:277whether or not[integrations.gpt_diagnostics]exists. The shippedtrusted-server.example.tomlleaves that section commented out (line 619), so this is the default state.A small program against a4e01eb loaded settings from
trusted-server.example.toml(only the EC passphrase placeholder replaced), then calledgpt_diagnostics::prepare_requestandcookies::handle_request_cookies. Header bytes for A werets-ec=abc.Ab1234; city=M\xc3\xbcnchen:Whichever resolution is chosen, the adapter-level test should use a valid non-ASCII UTF-8 value as well as invalid bytes, and should run with
gpt_diagnosticsunconfigured.