Make a third-party MCP session id safe for the gateway topic - #8565
Merged
Merged
Conversation
The session id becomes one level of the gateway's MQTT topic. openai/session is shaped `v1/<token>`, so embedding it raw added an extra level, the topic then matched no ACL pattern, and the publish was denied without raising a validation error. The request never reached the gateway and the caller waited out the full 30s proxy timeout. Sanitise that value before use: ids already safe for a topic level pass through, anything else is hashed. Hashed rather than stripped so two sessions cannot collapse onto one topic, and stably so a pinned browser tab survives between a client's own requests. Only the _meta value is treated as untrusted. The header is an id we minted coming back to us and randomUUID is ours. Also swap the ACL's session length check for a format check, as a backstop for anything that reaches the topic by another route.
andypalmi
approved these changes
Sep 18, 2026
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8565 +/- ##
==========================================
+ Coverage 77.02% 77.03% +0.01%
==========================================
Files 465 466 +1
Lines 24923 24934 +11
Branches 6636 6638 +2
==========================================
+ Hits 19197 19208 +11
Misses 5726 5726
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #8564
_meta["openai/session"]is shapedv1/<token>. The session id becomes one level of the gateway's MQTT topic, so embedding that raw produced a topic with an extra level, which matched no ACL pattern and was denied without raising a validation error. The request never reached the broker or the gateway, and the caller waited out the full 30s proxy timeout before getting a 504. Production showedresult: denyin 0.226 ms for those topics while neighbouring UUID-carrying requests from the same user were allowed and completed in ~150 ms.The fix sanitises that value before it is used. Ids already safe as a topic level pass through untouched, so a well-behaved client's session id stays readable in logs and on the wire. Anything else is replaced with a SHA-256 digest. Hashed rather than stripped for two reasons: stripping would collapse two distinct sessions onto the same topic, and the mapping has to be stable or a pinned browser tab is unreachable between a client's own requests.
Only the
_metavalue is treated as untrusted. Themcp-session-idheader is an id we minted coming back to us, andrandomUUID()is ours, so neither is touched. The helper returnsnullwhen there is nothing usable, which lets a missingopenai/sessionfall through to a fresh id without an extra branch.The ACL's session check moves from length (
mcpSessionId.length < 8) to format. Worth being precise about what that adds: a slashed id never matched the topic pattern([^/]+)in the first place, so it was already denied, just silently. The case the format check genuinely catches is something likesess+ion12345, long enough for the old check and a clean match for the topic pattern, but an MQTT wildcard sitting inside a topic level. It is a backstop, not the fix.Tests: seven on the helper (passthrough, separator, wildcards, stability, distinctness, idempotence, unusable input), two on the route (a slashed
openai/sessionyields a topic-safe id echoed in the response header, and the same value routes identically across two calls), two on the ACL.One known gap, left deliberately:
mcp-session-idis still a client-supplied header, so a client sendingmcp-session-id: a/bwould rebuild the same broken topic and eat the timeout. That is self-inflicted rather than a security issue, the ACL denies it either way, and the clients actually affected here do not send the header at all.