Repository navigation
Edge Cookie identifiers leave the edge without an ownership check on the proxy and testlight paths #1096
Description
Found while reviewing the Edge Cookie provider work in
PR #1043. Neither of
the two reviews on that pull request raised it.
The problem
On main at commit d516a9e94, the live helper
edge_cookie::get_ec_id hands back any value a request carries in the
x-ts-ec header or the ts-ec cookie provided the value passes a character
allowlist, and three production paths then send that value onward. Nothing
checks that this deployment issued the value, so a browser carrying a
cookie-safe string of someone else's choosing has that string forwarded to the
origin, to click targets, and to a partner endpoint.
This is the same trap Aram flagged in review on the dead helper
ec::get_ec_id, but on paths that run in production.
What was checked and found
Read on main at d516a9e94.
| Where | What is there |
|---|---|
crates/trusted-server-core/src/edge_cookie.rs:64-94 |
get_ec_id returns the x-ts-ec request header value, then the ts-ec cookie value, if ec_id_has_only_allowed_chars passes |
crates/trusted-server-core/src/ec/cookies.rs:27-40 |
ec_id_has_only_allowed_chars is a character allowlist alone, being [A-Za-z0-9._-], with no length bound and no format check |
crates/trusted-server-core/src/proxy.rs:1264 |
append_ec_id puts the value on the outbound origin URL as the ts-ec query parameter |
crates/trusted-server-core/src/proxy.rs:1609 |
the click and redirect path puts the value on the redirect target |
crates/trusted-server-core/src/integrations/testlight.rs:189 |
the testlight integration posts the value as user.id |
The x-ts-ec source is worth calling out separately, because a request header
is set by whoever made the request, so the value need not have come from a
cookie this deployment ever wrote.
Why it matters
Any value that fits the alphabet is egressed as though Trusted Server had
issued it. That includes an identifier created by a different Trusted Server
deployment and an identifier a page script chose. The pluggable-providers
design says the opposite, being that a value the selected provider does not
recognize is never used or egressed.
What resolves it
PR #1043 already
carries the fix, in commit 8684c6954 ("Egress only an Edge Cookie identifier
the provider recognizes"). On that branch:
crates/trusted-server-core/src/edge_cookie.rsaddsrecognized_ec_id, which
takes the raw inbound value and then dispatches the{code}~prefix to the
provider that owns it, returningNonefor a foreign code, for a value the
selected provider rejects, and for every value when the deployment selects no
provider at all.- The proxy origin and click paths in
crates/trusted-server-core/src/proxy.rs
callrecognized_ec_idinstead of the raw reader. - The testlight integration calls it too and fails the request rather than
proxying when no recognized identifier is present.
So this issue is only resolvable by merging
PR #1043. It is filed
so the change is traceable rather than buried in a 44-file pull request, and it
should be closed when that pull request merges.
The related residue is resolved on the same branch. get_ec_id was public and
read the client-settable x-ts-ec request header directly, and commit
252aadefc ("Stop exposing an inbound Edge Cookie identifier nothing has
vouched for") makes it pub(crate) and renames it
unvalidated_ec_id_from_request, so the only way in from outside the module is
the checked recognized_ec_id path. That change is the inbound twin of this
egress fix and is written up in full in #1095.
Note on authorship
This issue was drafted with AI assistance. Every file and line reference above
was read at the commits named. A human should review it before acting on it.
Activity
jwrosewell commentedon Aug 31, 2026
Fixed by #1043. The review response on #1043 names the commit that answers it. This issue is filed against
main, so it can be closed once #1043 merges.