Skip to content

mcp_json_rest_bridge: percent-encode '.' and '/' in path-template values (fixes #45931)#45949

Open
omkhar wants to merge 4 commits into
envoyproxy:mainfrom
omkhar:fix-mcp-path-injection-45931
Open

mcp_json_rest_bridge: percent-encode '.' and '/' in path-template values (fixes #45931)#45949
omkhar wants to merge 4 commits into
envoyproxy:mainfrom
omkhar:fix-mcp-path-injection-45931

Conversation

@omkhar

@omkhar omkhar commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

The mcp_json_rest_bridge HTTP filter builds the upstream REST request URL by substituting values from the (untrusted) JSON-RPC arguments object into a configured path template. constructBaseUrl() percent-encodes each substituted value with:

Http::Utility::PercentEncoding::encode(jsonValueToString(*template_value_json), ReservedChars);

ReservedChars (http_request_builder.h) left [-_./0-9a-zA-Z] unescaped. Because . and / were unescaped, an attacker-controlled template value could inject additional path segments into the upstream URL.

Impact

For a template /v1/users/{userId}/profile, a value of ../../admin yields /v1/users/../../admin/profile (path traversal) and 7/actions/delete yields /v1/users/7/actions/delete/profile (segment injection). The value originates from the untrusted MCP request body, so a client can reach REST resources/verbs outside the single path segment the template author intended (CWE-22 / CWE-88).

Reproduction

Substituting the routines (PercentEncoding::encode, constructBaseUrl, ReservedChars) into a standalone harness shows the injection through ordinary filter use:

arg userId=alice            -> /v1/users/alice/profile
arg userId=../../admin      -> /v1/users/../../admin/profile          (injection)
arg userId=7/actions/delete -> /v1/users/7/actions/delete/profile     (injection)

Fix

Add . and / to ReservedChars so path separators in a substituted path-template value are percent-encoded. This matches the Google API path-template semantics (a single {var} binds one path segment) and prevents segment/traversal injection. After the fix the same inputs encode safely and benign values are unchanged:

arg userId=alice            -> /v1/users/alice/profile
arg userId=../../admin      -> /v1/users/%2E%2E%2F%2E%2E%2Fadmin/profile
arg userId=7/actions/delete -> /v1/users/7%2Factions%2Fdelete/profile

Fixes #45931

The filter substitutes values from the untrusted JSON-RPC `arguments` object
into a configured REST URL path template. `constructBaseUrl()` percent-encodes
each value with `ReservedChars`, which left `.` and `/` unescaped, so a value
such as `../../admin` or `7/actions/delete` injects additional path segments
into the upstream request URL (path traversal / segment injection reachable
through ordinary use of the filter). Add `.` and `/` to `ReservedChars` so a
substituted path-template value binds a single segment, matching the Google
API path-template semantics. Benign values are unchanged.

Fixes envoyproxy#45931

Signed-off-by: Omkhar Arasaratnam <omkhar@linkedin.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Omkhar Arasaratnam and others added 3 commits July 2, 2026 18:31
Signed-off-by: Omkhar Arasaratnam <omkhar@linkedin.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Only *simple* template variables ({id}) are restricted to a single segment
(escape / and .); wildcard variables ({name=projects/*}) keep multi-segment
Google-API resource-name values, fixing the regression against the existing
wildcard-template tests while still closing the injection in envoyproxy#45931.

Signed-off-by: Omkhar Arasaratnam <omkhar@linkedin.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…iables

Instead of silently encoding, a simple template variable ({id}) now rejects a
value containing '/' or equal to '.'/'..', failing closed rather than rewriting
the non-re-normalized upstream path. Wildcard variables ({name=projects/*}) keep
their multi-segment Google-API resource-name values (issue envoyproxy#45931).

Signed-off-by: Omkhar Arasaratnam <omkhar@linkedin.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tyxia tyxia self-assigned this Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Envoy mcp_json_rest_bridge filter injects unescaped path segments into the upstream request path (unauthenticated)

2 participants