fix: prevent path traversal in style and tileset tool URL construction#103
Merged
Conversation
Five tools (RetrieveStyle, DeleteStyle, UpdateStyle, PreviewStyle, TilequeryTool) concatenated user-supplied path parameters directly into Mapbox API URLs without validation or encoding. Because Node.js fetch uses the WHATWG URL parser, `../` sequences were normalized before sending, allowing requests to reach unintended API endpoints. Changes: - Add shared `styleIdSchema` with allowlist regex that rejects path separators, dots, percent-encoded sequences, and null bytes - Apply `styleIdSchema` to all four style tools via a shared module (src/tools/shared/styleId.schema.ts) - Add format validation to TilequeryTool tilesetId (owner.name format) - Wrap both username and styleId/tilesetId in `encodeURIComponent` at every URL construction site (defense-in-depth) - Replace silent fallback in output schema validation with explicit `isError: true` responses across all API tools, preventing unintended API responses from being forwarded to callers - Remove now-unused BaseTool.validateOutput() method - Add test/security/path-traversal.test.ts with 52 tests covering schema rejection, valid ID acceptance, URL encoding, and response schema mismatch behavior
…rades - Reformat dynamic imports to match Prettier 3.8.x style - Update Zod v4 error code assertions (invalid_value, too_big) - Update mimeType assertions for @mcp-ui/server v6.1.0 (text/html;profile=mcp-app) - Fix PreviewStyleTool test mimeType expectation
zmofei
force-pushed
the
fix/path-traversal-security
branch
from
May 4, 2026 11:52
cc9ee0b to
11cdd28
Compare
- Validate that pagination next-page URLs from Link response headers share the same origin as the configured API endpoint; cross-origin URLs are rejected to prevent access token exfiltration - Add redactToken() utility that strips access_token query parameter values from strings before they reach log output or MCP client error responses (network errors include the full request URL in their message which would otherwise expose the token) - Remove full URL from info/debug level pagination log messages
zmofei
force-pushed
the
fix/path-traversal-security
branch
from
May 4, 2026 12:14
8bb1936 to
064a3a1
Compare
zmofei
marked this pull request as ready for review
May 4, 2026 12:21
ctufts
reviewed
May 4, 2026
ctufts
reviewed
May 4, 2026
ctufts
reviewed
May 4, 2026
ctufts
reviewed
May 4, 2026
ctufts
approved these changes
May 4, 2026
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.
Summary
Five MCP tools concatenated user-supplied path parameters directly into Mapbox API URLs without input validation or URL encoding. Because Node.js
fetchuses the WHATWG URL parser,../sequences in astyleIdortilesetIdinput were normalized client-side before the request was sent — allowing the request to reach unintended API endpoints.This PR applies a two-layer defense, hardens output schema validation across all affected tools, and adds two additional security fixes discovered during review.
Changes
Input validation — allowlist regex on all path parameters
src/tools/shared/styleId.schema.ts— shared Zod schema with regex/^[a-z0-9][a-z0-9-]*[a-z0-9]$/, which accepts both user-owned style IDs and built-in Mapbox style names (standard,streets-v12,navigation-day-v1, etc.) while rejecting path separators, dots, percent-encoded sequences, and null bytesstyleIdSchematoDeleteStyleTool,PreviewStyleTool,RetrieveStyleTool, andUpdateStyleToolinput schemas via the shared moduleTilequeryTooltilesetIdinput:/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/(enforcesowner.tileset-nameformat)URL encoding —
encodeURIComponentat every construction siteWrap both
username(extracted from JWT) andstyleId/tilesetIdinencodeURIComponentat every URL construction site across all five tools. This encodes/→%2F, preventing any remaining path segments from being interpreted as directory traversal by the URL parser.Affected files:
DeleteStyleTool.ts,PreviewStyleTool.ts(URL + resource URI),RetrieveStyleTool.ts,TilequeryTool.ts,UpdateStyleTool.tsOutput schema validation — hard-fail on mismatch
Replace the previous silent fallback behavior (returning raw unvalidated API responses) with explicit
isError: trueresponses when the API response does not match the declared output schema. This prevents unintended API responses from being forwarded to callers.Applied to:
RetrieveStyleTool,UpdateStyleTool,TilequeryTool,ListStylesTool,ListTokensTool,CreateTokenToolRemove now-unused
BaseTool.validateOutput()method.Cross-origin Link header rejection (ListTokensTool)
ListTokensToolauto-paginates by following theLink: rel=nextresponse header. Without origin validation, a malicious or compromised response could redirect pagination to an attacker-controlled host and exfiltrate the access token via the appendedaccess_tokenquery parameter.Added origin validation: next-page URLs whose origin does not match the configured API endpoint are rejected with a warning log. Pagination stops rather than following a cross-origin redirect.
Token redaction from logs and error messages
Added
redactToken()utility that stripsaccess_token=...query parameter values from strings before they reach log output or MCP client error responses. Network errors (DNS failure, timeout) from Node.js include the full request URL in their message — without redaction, the access token would be forwarded to callers in error responses.Applied to: catch-block error messages in
MapboxApiBasedTool.run(), debug-level URL log inListTokensTool.Tests
test/security/path-traversal.test.tswith 52 tests covering schema rejection, valid ID acceptance, URL encoding, and response schema mismatch behaviorListTokensTool.test.ts@mcp-ui/serverv6.1.0 mimeType changesTest plan
npm testpasses (576/576)npx tsc --noEmit— zero errorsnpx vitest run test/security/path-traversal.test.ts— 52/52 pass