multistore_sts::build_sts_error_response interpolates the error message into the <Message> element unescaped:
let xml = format!("...<Code>{}</Code><Message>{}</Message>...", code, message);
Several messages carry caller-controlled text:
ProxyError::RoleNotFound(role_arn) → role not found: {role_arn}
find_key → key '{kid}' not found in JWKS, where kid comes from the unverified JWT header
verify_token errors that echo header fields such as alg
The body is served as application/xml. A host's CORS layer may add access-control-allow-origin: *.
Impact:
- Script injection: a
RoleArn such as <x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(document.domain)</x:script> returns a document that browsers render as XHTML, running script on the proxy's origin.
- Unparseable errors: any
& in a RoleArn makes the error body malformed XML, so SDKs fail to parse it.
This is reachable through the STS route that Router::with_sts mounts. A host can escape errors it builds itself, but not this route's.
Suggested fix: escape &, < and > (and " if attributes are ever added) in message before formatting. The Code values are fixed strings.
Found in review of source-cooperative/data.source.coop#237. Its own exchange paths now build escaped bodies (source-cooperative/data.source.coop#246); the person route still goes through this builder.
multistore_sts::build_sts_error_responseinterpolates the error message into the<Message>element unescaped:Several messages carry caller-controlled text:
ProxyError::RoleNotFound(role_arn)→role not found: {role_arn}find_key→key '{kid}' not found in JWKS, wherekidcomes from the unverified JWT headerverify_tokenerrors that echo header fields such asalgThe body is served as
application/xml. A host's CORS layer may addaccess-control-allow-origin: *.Impact:
RoleArnsuch as<x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(document.domain)</x:script>returns a document that browsers render as XHTML, running script on the proxy's origin.&in aRoleArnmakes the error body malformed XML, so SDKs fail to parse it.This is reachable through the STS route that
Router::with_stsmounts. A host can escape errors it builds itself, but not this route's.Suggested fix: escape
&,<and>(and"if attributes are ever added) inmessagebefore formatting. TheCodevalues are fixed strings.Found in review of source-cooperative/data.source.coop#237. Its own exchange paths now build escaped bodies (source-cooperative/data.source.coop#246); the person route still goes through this builder.