Skip to content

fix(sts): escape caller text in exchange error bodies - #246

Merged
alukach merged 6 commits into
feat/platform-trustfrom
fix/sts-xml-escape
Oct 1, 2026
Merged

alukach merged 6 commits into
feat/platform-trustfrom
fix/sts-xml-escape

Conversation

@alukach

@alukach alukach commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Note

Stacked on #237. Until #237's branch has its latest review fixes pushed, this diff also shows those five commits; the change here is the last commit alone.

What I'm changing

The API-key and platform-token paths at /.sts now escape their error messages. Those messages can carry text the caller sent, RoleArn or a token's kid or alg, and multistore's build_sts_error_response wrote it unescaped into an application/xml body served with access-control-allow-origin: *. So:

  • markup in RoleArn ran as XHTML on the proxy's origin, for example <x:script xmlns:x="http://www.w3.org/1999/xhtml">…</x:script>;
  • an & in RoleArn made the body unparseable for SDKs.

How I did it

  • sts_refusal maps a ProxyError to status, code and message the way multistore's builder does, then builds the body with sts_error_xml.
  • sts_error_xml, which every error on these two paths goes through, escapes &, < and > in the message.
  • build_sts_error_response is no longer used in this crate.

Not covered: the person issuer's route. Its errors come from multistore's router (with_sts), which calls the same unescaped builder, so the fix there belongs in multistore's build_sts_error_response.

How to test it

tests/test_platform_trust.py::test_a_refusal_escapes_what_the_caller_sent sends a forged GitHub token with a RoleArn containing an XHTML script element and &. It checks that the reply parses as XML and that the message carries the RoleArn as text. Locally against wrangler dev and the stub, 47 tests pass and 16 skip (the skipped ones need a GitHub token).

PR Checklist

Related Issues

Follows the review of #237.

🤖 Generated with Claude Code

alukach and others added 6 commits September 30, 2026 23:27
A token file written with `jq -r … > file` ends in a newline, which SDKs
send as-is and the signature's base64 decode rejects with a 400.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The separate refused key cost an extra Cache API match before every
lookup, and the delete of the positive entry never found anything after
a 403. A refusal is now stored as {"trusted":false} under the same key
for REFUSED_TRUST_CACHE_SECS: one Cache API read per exchange.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Platform-token refusals and the API key path's non-key errors came from
build_sts_error_response without it, and SDKs show only the message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The platform path takes its issuers' tokens ahead of the STS route, so
an entry for AUTH_ISSUER would refuse every person exchange. Drop it at
load with an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fail-closed check ran before the API-key and platform short-circuits,
so an empty AUTH_AUDIENCE also disabled exchanges that carry their own
audience checks (platform) or none at all (keys).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RoleArn and a token's kid or alg reached the <Message> of an
application/xml response unescaped, through multistore's
build_sts_error_response: markup there ran as XHTML on the proxy origin,
and an & made the body unparseable for SDKs. The API-key and platform
paths now build their errors here with the message escaped. The person
route's errors still come from multistore's builder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @alukach's task in 16s —— View job


I'll analyze this and get back to you.


💰 Estimated review cost: $0.12 · 0m15s · 3 turns

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown

🚀 Latest commit deployed to https://source-data-proxy-pr-246.source-coop.workers.dev

  • Date: 2026-10-01T06:38:17Z
  • Commit: f7d1402

@alukach
alukach marked this pull request as ready for review October 1, 2026 06:47
@alukach
alukach merged commit ec3f79c into feat/platform-trust Oct 1, 2026
11 checks passed
@alukach
alukach deleted the fix/sts-xml-escape branch October 1, 2026 06:47

This branch was successfully deployed

1 active deployment
preview — cb808f65 Deployed Oct 1, 2026 by alukach via Deploy & Test / Deploy #399
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant