Skip to content

fix: Encode userId path parameter in user API key methods#1664

Open
devin-ai-integration[bot] wants to merge 3 commits into
mainfrom
feature/sec-2175-user-management-api-key-methods-unencoded-userid-path
Open

fix: Encode userId path parameter in user API key methods#1664
devin-ai-integration[bot] wants to merge 3 commits into
mainfrom
feature/sec-2175-user-management-api-key-methods-unencoded-userid-path

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Description

Automated first-pass security fix for SEC-2175. Requires human security review before merge.

createUserApiKey and listUserApiKeys interpolated the userId argument directly into the request path without encodeURIComponent:

`/user_management/users/${userId}/api_keys`

Because HttpClient.getResourceURL builds the URL with new URL(path, baseURL) (src/common/net/http-client.ts:83), a userId containing ../ segments and a ? is normalized and query-split by the WHATWG URL parser. This lets a userId that reaches these methods redirect the SDK's environment-key-authenticated request to an arbitrary API path.

Reproduced locally against a mock HTTP server (no production traffic), observing the actual outbound request:

createUserApiKey("../../../organizations/org_01TARGET/api_keys?")
  -> POST /organizations/org_01TARGET/api_keys   (Authorization: Bearer sk_... attached)

The fix wraps userId in encodeURIComponent, matching the SDK's established pattern (e.g. getUserConnectedAccount at src/pipes/pipes.ts). After the fix the same input stays confined to the route template:

  -> POST /user_management/users/..%2F..%2F..%2Forganizations%2Forg_01TARGET%2Fapi_keys%3F/api_keys

Added regression tests for both methods asserting the encoded pathname.

Note: this is a client-side SDK defect. The full confused-deputy impact (whether the WorkOS API accepts the redirected request and mints an org-scoped key) depends on server-side behavior that was not tested against production; encoding the parameter closes the SDK-side primitive regardless.

Documentation

Does this require changes to the WorkOS Docs? E.g. the API Reference or code snippets need updates.

[ ] No

Link to Devin session: https://app.devin.ai/sessions/19376786c7a44cceb39716ee31a45cfb

createUserApiKey and listUserApiKeys interpolated the userId argument
directly into the request path without encodeURIComponent. Because the
HTTP client builds the URL with new URL(path, baseURL), a userId
containing ../ segments and a ? was normalized and query-split, letting
the SDK's environment-key-authenticated request be redirected to an
arbitrary API path (e.g. /organizations/org_.../api_keys).

Wrap userId in encodeURIComponent, matching the SDK's established
pattern (e.g. getUserConnectedAccount), so the argument stays confined
to the user resource route. Add regression tests.
@devin-ai-integration
devin-ai-integration Bot requested review from a team as code owners July 23, 2026 18:47
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author
Original prompt from Linear User

Please work on ticket "user-management API key methods: unencoded userId path parameter enables WorkOS API request forgery (confused deputy) that can mint org-scoped API keys" (SEC-2175)

@playbook:playbook-b588614117c7477a9b9729928385384f

@devin-ai-integration
devin-ai-integration Bot requested a review from mattgd July 23, 2026 18:47
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@linear-code

linear-code Bot commented Jul 23, 2026

Copy link
Copy Markdown

SEC-2175

@devin-ai-integration devin-ai-integration Bot changed the title Encode userId path parameter in user API key methods fix: Encode userId path parameter in user API key methods Jul 23, 2026
@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a path-traversal vulnerability where userId was interpolated unencoded into URL paths in user-management.ts, allowing WHATWG URL normalization to redirect SDK requests to arbitrary API routes. All eleven call sites in user-management.ts are correctly patched with encodeURIComponent.

  • user-management.ts: All user-scoped path interpolations (getUser, sendVerificationEmail, verifyEmail, updateUser, listSessions, deleteUser, listUserApiKeys, createUserApiKey, getUserIdentities) now wrap userId in encodeURIComponent.
  • user-management.spec.ts: Regression tests are added for five of the fixed methods, verifying the encoded pathname is used in the outbound request.
  • The same unencoded userId pattern remains in multi-factor-auth.ts and feature-flags.ts, presenting the identical path-traversal surface on those routes.

Confidence Score: 3/5

The fix is correct for user-management.ts but the same path-traversal pattern remains live in multi-factor-auth.ts and feature-flags.ts, leaving the attack surface partially open.

The fix is correct and complete within user-management.ts, but the identical unencoded userId interpolation pattern remains in multi-factor-auth.ts (createUserAuthFactor, listUserAuthFactors) and feature-flags.ts (listUserFeatureFlags), all callable with the SDK environment key. The partial fix reduces but does not eliminate the attack surface.

src/multi-factor-auth/multi-factor-auth.ts (lines 171, 200, 207) and src/feature-flags/feature-flags.ts (lines 189, 196) need the same encodeURIComponent fix applied.

Important Files Changed

Filename Overview
src/user-management/user-management.ts Wraps every userId path interpolation in encodeURIComponent across all eleven affected call sites. Fix is complete and consistent with the established SDK pattern.
src/user-management/user-management.spec.ts Adds path-traversal encoding regression tests for five of the nine fixed methods; sendVerificationEmail, verifyEmail, listSessions, and getUserIdentities lack corresponding encoding assertions.

Reviews (3): Last reviewed commit: "Encode userId in updateUser path" | Re-trigger Greptile

Extend the encodeURIComponent fix to the other user-scoped endpoints
that share the same raw-interpolation path-traversal primitive: getUser,
sendVerificationEmail, verifyEmail, listSessions, deleteUser, and
getUserIdentities. Add regression tests for getUser and deleteUser.
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Good catch on the incomplete fix. The same raw ${userId} interpolation was indeed present in six other methods (getUser, sendVerificationEmail, verifyEmail, listSessions, deleteUser, getUserIdentities), which share the same HTTP client and traversal primitive.

Addressed in 088d805: all userId path interpolations in user-management.ts now use encodeURIComponent, so the whole class is closed in this file rather than just the two API-key methods. Added regression tests for getUser (GET) and deleteUser (DELETE) in addition to the API-key ones. Still an automated first-pass fix pending human security review.

updateUser interpolated payload.userId raw into the PUT path, leaving
the same path-traversal primitive open. Encode it and add a regression
test.
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Fixed the updateUser gap in 17708af — it interpolated payload.userId raw (not ${userId}), which is why it wasn't caught in the previous pass. Now uses encodeURIComponent(payload.userId) with a regression test. All userId/payload.userId path interpolations in user-management.ts are encoded.

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Confirmed the same unencoded pattern exists in multi-factor-auth.ts (userId at lines 171/200/207) and feature-flags.ts (userId at 189/196), plus other identifiers there (slug, targetId, organizationId). These are the identical path-traversal primitive on other routes.

Scoping note: SEC-2175 is specifically the user-management API-key methods, and this PR already extends the fix to cover all userId interpolations in user-management.ts. The multi-factor-auth/feature-flags (and other-identifier) call sites are a broader SDK-wide remediation that I'm intentionally not bundling into this security PR — better tracked as a separate follow-up so this fix stays reviewable and scoped. Flagging to the security team to decide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

0 participants