fix: directions_tool exclude parameter could inject query parameters - #234
Merged
Merged
Conversation
A point(<lng> <lat>) exclude entry was validated by splitting on spaces
and reading only the first two tokens, so any extra content after them
(e.g. "point(0 0 &injected=evil)") was never inspected or rejected.
That value then reached the outbound Directions API request through a
hand-rolled encoder that escaped ",", "(", ")", and space but not "&"
or "=", concatenated directly onto the query string instead of going
through URLSearchParams like every other parameter. Together these let
a caller-supplied exclude value add or override arbitrary query
parameters on the authenticated request.
Fixes both defects: point(...) entries must now be exactly two numbers
and nothing else, and exclude is now appended via URLSearchParams (in
both the server-side request builder and its hand-ported client-side
twin) so it's always correctly percent-encoded regardless of content.
ctufts
approved these changes
Jul 29, 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.
What changed
Fixes a query-parameter-injection defect in
directions_tool'sexcludeparameter, present in both the server-side request builder and its hand-ported client-side twin in the map preview iframe.The underlying issue
A
point(<lng> <lat>)exclude entry was validated by splitting on spaces and destructuring only the first two tokens:Any extra content after those two tokens — e.g.
point(0 0 &injected=evil)— was silently dropped by the destructuring and never inspected or rejected.That raw value then reached the outbound Mapbox Directions API request through a hand-rolled encoder that escaped
,,(,), and space, but not&or=:...and was concatenated directly onto the query string rather than through
URLSearchParams, which every other parameter uses:Together, these two defects let a caller-supplied
excludevalue add new query parameters or override existing ones (e.g.alternatives) on the authenticated Directions API request.Fix
DirectionsTool.input.schema.ts): apoint(...)entry's interior must now match/^(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)$/— exactly two numbers and nothing else — instead of accepting anything past the first two space-separated tokens.buildDirectionsRequestUrl.tsand its client-side twin indirectionsAppHtml.ts):excludeis now appended viaqueryParams.append('exclude', input.exclude)like every other parameter, instead of a hand-rolled encoder + string concatenation.URLSearchParamspercent-encodes the full character set correctly, including&/=, which the custom encoder missed.Applying the fix to both the server builder and the client-side self-fetch twin closes it at both call sites, even though the client twin already had an independent
isSafeDirectionsParamsregex guard that happened to reject&/=before reaching this code path.Testing
point(...)entry with trailing content is now rejected by the schema with a clear message, and that the URL builder itself can no longer inject/duplicate a query parameter even if a malformed value reached it directly.URLSearchParamsencodes a literal space as+rather than the old encoder's%20— both are standard and decode identically server-side.Note on scope
This only touches
directions_tool'sexcludehandling. I checked for the same anti-pattern (hand-rolled encoder + manual string concatenation bypassingURLSearchParams) elsewhere in the codebase — it doesn't appear anywhere else.