Skip to content

The non-UTF-8 cookie error in the Edge Cookie path cannot be reached on any adapter #1098

Description

@jwrosewell

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-67 is handle_request_cookies.
    It calls HeaderValue::to_str on the Cookie header and maps the failure to
    TrustedServerError::InvalidHeaderValue with the message "Cookie header
    contains invalid UTF-8".
  • crates/trusted-server-core/src/ec/mod.rs:104 is 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-416 is
    sanitize_console_cookie. It reads every Cookie field line through
    to_str().ok(), dropping any line that is not UTF-8, removes the Cookie
    header outright, and reinserts only the retained lines.
  • sanitize_console_cookie is called from prepare_request at
    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_request before it builds Edge
    Cookie state.
    • Fastly, crates/trusted-server-adapter-fastly/src/app.rs:585, immediately
      before build_ec_request_state at :592.
    • Axum, crates/trusted-server-adapter-axum/src/app.rs:146 in the shared
      request wrapper and :207 on the fallback path.
    • Cloudflare, crates/trusted-server-adapter-cloudflare/src/app.rs:200.
    • Spin, crates/trusted-server-adapter-spin/src/app.rs:577 and :718, with
      build_ec_context following at :590.
  • One caller can still meet a non-UTF-8 Cookie header, being the Fastly admin
    Edge Cookie lookup at
    crates/trusted-server-adapter-fastly/src/app.rs:576, which is dispatched
    before prepare_request runs 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.

  1. An error path that cannot run is untested behavior that reads as tested. Any
    future reader takes the Edge Cookie code as handling a hostile Cookie
    header when in fact something else already handled it.
  2. 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.
  3. 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, log it at warn
    level in sanitize_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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions