Skip to content

INTER-2499 fix: url encode path parameters - #282

Merged
JuroUhlar merged 9 commits into
mainfrom
fix/INTER-2499-encode-path-params
Sep 10, 2026
Merged

INTER-2499 fix: url encode path parameters#282
JuroUhlar merged 9 commits into
mainfrom
fix/INTER-2499-encode-path-params

Conversation

@JuroUhlar

@JuroUhlar JuroUhlar commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Fixes INTER-2499. Path params were interpolated raw, so getEvent('../events') requested /v4/events.

  • Encode path params with encodeURIComponent in getRequestPath, the choke point for getEvent, updateEvent and deleteVisitorData.
  • Reject . and .. with a TypeError.
  • Unit tests for the encoding table; wire-level tests per operation asserting the exact fetch URL and host.

Real IDs are encodeURIComponent no-ops, so nothing changes on the wire for legitimate traffic.

Discussion point: why . and .. throw instead of being encoded

The URL Standard treats %2e as a dot when matching dot-path segments, so new URL() drops the segment for .. and %2E%2E alike. A literal dot segment is unrepresentable, so throwing is the only option.

@changeset-bot

changeset-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 66c2cb4

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements
98.21% (+0.07% 🔼)
165/168
🟢 Branches
97.03% (+0.19% 🔼)
98/101
🟢 Functions 100% 35/35
🟢 Lines
98.19% (+0.08% 🔼)
163/166

Test suite run success

130 tests passing in 30 suites.

Report generated by 🧪jest coverage report action from 66c2cb4

Show full coverage report
St File % Stmts % Branch % Funcs % Lines Uncovered Line #s
🟢 All files 98.21 97.02 100 98.19
🟢  src 98.5 98.76 100 98.49
🔴   ...edApiTypes.ts 0 0 0 0
🔴   index.ts 0 0 0 0
🟢   sealedResults.ts 100 100 100 100
🟢   ...rApiClient.ts 96.15 97.22 100 96.15 358,362
🟢   types.ts 100 100 100 100
🟢   urlUtils.ts 100 100 100 100
🟢   webhook.ts 100 100 100 100
🟢  src/errors 97.05 90 100 96.96
🟢   apiErrors.ts 100 100 100 100
🟢   ...orResponse.ts 100 100 100 100
🟢   toError.ts 87.5 88.88 100 87.5 21
🟢   unsealError.ts 100 50 100 100 10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The changeset release note doesn’t mention the new dot-segment (./..) rejection behavior, which is user-visible and should be documented for consumers.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR hardens URL construction for the Fingerprint Server API client by ensuring path parameters cannot escape their intended single path segment, preventing path traversal/endpoint confusion during request building.

Changes:

  • URL-encodes all path parameters in getRequestPath via a dedicated encodePathParam helper.
  • Rejects dot-segment path parameters (. and ..) by throwing TypeError to avoid URL normalization changing the requested endpoint.
  • Adds unit and mocked wire-level tests asserting the exact URL passed to fetch and that the host cannot be altered via path params.
File summaries
File Description
src/urlUtils.ts Adds encodePathParam and applies it to placeholder replacement in getRequestPath.
tests/unit-tests/urlUtilsTests.spec.ts Adds a table-driven unit suite validating path-param encoding and dot-segment rejection.
tests/mocked-responses-tests/pathParamEncodingTests.spec.ts Adds integration-style tests ensuring client operations request the expected encoded URL and never change host.
.changeset/encode-path-parameters.md Adds a changeset entry describing the release note for this behavior.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .changeset/encode-path-parameters.md Outdated
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There is a validated bypass for the dot-segment rejection when non-primitive (truthy) path params are passed by untyped JS callers, which can reintroduce the normalization issue.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread src/urlUtils.ts Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@JuroUhlar
JuroUhlar force-pushed the fix/INTER-2499-encode-path-params branch from 5592f65 to 83e57e1 Compare September 7, 2026 14:18
An untyped caller could pass a value that is not a string but stringifies to a dot segment, bypassing the rejection.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The change is localized to the URL construction choke point, aligns with the stated security intent, and is backed by both unit and wire-level tests covering key edge cases.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@JuroUhlar JuroUhlar changed the title fix: url encode path parameters INTER-2499 fix: url encode path parameters Sep 7, 2026
encodeURIComponent throws URIError on a lone surrogate, which escaped as a third error type at the public boundary. Also unifies the missing-parameter throw on TypeError.
Replace placeholders in one pass with a function replacement, move all validation into encodePathParam, and drop test rows that were duplicated across both paths.
@JuroUhlar
JuroUhlar marked this pull request as ready for review September 7, 2026 15:27
@JuroUhlar
JuroUhlar requested a lite review from Copilot September 7, 2026 15:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

encodePathParam can leak unexpected exceptions from String(value) coercion (e.g., Symbols or throwing toString), bypassing the intended consistent TypeError behavior.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread src/urlUtils.ts Outdated
String() throws for values with no primitive representation, which leaked as its own error type.
@JuroUhlar
JuroUhlar marked this pull request as draft September 7, 2026 15:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The security fix is localized to URL construction and is backed by both unit-level and wire-level tests, with only a minor doc-comment clarity note.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/urlUtils.ts:48

  • The encodePathParam doc comment mentions “valid event IDs” even though the helper is generic (also used for visitor_id etc.), which can be misleading. Consider wording it in a placeholder-agnostic way while still noting why dots are preserved.
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@JuroUhlar
JuroUhlar marked this pull request as ready for review September 7, 2026 15:46

@mcnulty-fp mcnulty-fp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! Just a few minor comments about perhaps improving the tests and the implementation a bit.

Comment thread tests/unit-tests/urlUtilsTests.spec.ts
Comment thread tests/unit-tests/urlUtilsTests.spec.ts
Comment thread src/urlUtils.ts Outdated
Comment thread src/urlUtils.ts Outdated
Comment thread tests/unit-tests/urlUtilsTests.spec.ts
JuroUhlar and others added 3 commits September 10, 2026 10:24
Co-authored-by: Dan McNulty <212590662+mcnulty-fp@users.noreply.github.com>
Co-authored-by: Dan McNulty <212590662+mcnulty-fp@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Following releases will be created using changesets from this PR:

@fingerprint/node-sdk@7.7.1

Patch Changes

  • URL-encode path parameters; reject "." and ".." path parameters with a TypeError. (9bace3e)

@JuroUhlar
JuroUhlar merged commit cb4ac9f into main Sep 10, 2026
19 checks passed
@JuroUhlar
JuroUhlar deleted the fix/INTER-2499-encode-path-params branch September 10, 2026 12:46
JuroUhlar added a commit that referenced this pull request Sep 11, 2026
Fixes
[INTER-2499](https://fingerprintjs.atlassian.net/browse/INTER-2499) on
the `api-v3` line. Backport of #282. Path params were interpolated raw,
so `getEvent('../events/search')` requested `/events/search`.

- Encode path params with `encodeURIComponent` in `getRequestPath`, the
choke point for `getEvent`, `updateEvent`, `getVisits` (and the
deprecated `getVisitorHistory`) and `deleteVisitorData`.
- Reject `.` and `..` with a `TypeError`.
- Unit tests for the encoding table; wire-level tests per operation
asserting the exact `fetch` URL and host.

Real IDs are `encodeURIComponent` no-ops, so nothing changes on the wire
for legitimate traffic.

### Differences from #282

- `getRequestPath` on this branch keeps its generic signature and has no
`apiVersion` prefix, so the normalization guard compares `url.pathname`
against the path as-is.
- Tests are Jest, so the three cases carrying an error `cause` assert
the type and the message separately — `toThrow(new TypeError(...))`
compares `cause` too.
- The wire-level table also covers `getVisits`, which does not exist on
v4.

### Discussion point: why `.` and `..` throw instead of being encoded

The URL Standard treats `%2e` as a dot when matching [dot-path
segments](https://url.spec.whatwg.org/#double-dot-path-segment), so `new
URL()` drops the segment for `..` and `%2E%2E` alike. A literal dot
segment is unrepresentable, so throwing is the only option.


[INTER-2499]:
https://fingerprintjs.atlassian.net/browse/INTER-2499?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
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.

3 participants