fix: url-encode path parameters in php/node/python/kotlin emitters#75
Merged
gjtorikian merged 1 commit intomainfrom May 2, 2026
Merged
fix: url-encode path parameters in php/node/python/kotlin emitters#75gjtorikian merged 1 commit intomainfrom
gjtorikian merged 1 commit intomainfrom
Conversation
The php, node, python, and kotlin emitters were interpolating caller-supplied
ids directly into request paths without per-segment URL-encoding. A value
like "../webhook_endpoints/wh_target" was silently normalized by libcurl
(RFC 3986 dot-segment removal) before transmission, so the request reached a
different endpoint of the WorkOS API while still authenticated with the
application's bearer token — i.e. forged cross-resource API requests under
the application's API key.
Add a shared `parsePathTemplate` helper in `src/shared/path-template.ts` that
splits an OpenAPI path template into ordered literal/param segments. Each of
the four affected emitters now renders parameters through a per-language
URL-encoding call:
- php: `'orgs/' . rawurlencode($id) . '/foo'` (concatenation; PHP can't call
functions inside `"..."`)
- node: `` `orgs/${encodeURIComponent(id)}/foo` ``
- python: `f"orgs/{quote(str(id), safe='')}/foo"` (with auto-injected
`from urllib.parse import quote` import)
- kotlin: `"orgs/${encodePathSegment(id)}/foo"` (with auto-imported runtime
helper `com.workos.common.http.encodePathSegment` — the helper is added in
a separate workos-kotlin PR)
dotnet, go, and ruby were already encoding (Uri.EscapeDataString,
url.PathEscape, WorkOS::Util.encode_path) so no changes there.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Closes a path-traversal / cross-resource-API-call vulnerability in the php, node, python, and kotlin emitters.
The four affected emitters were interpolating caller-supplied ids directly into request paths without per-segment URL-encoding. A value like
../webhook_endpoints/wh_targetwas silently normalized by libcurl (RFC 3986 dot-segment removal) before transmission, so the SDK request reached a different endpoint of the WorkOS API while still authenticated with the application's bearer token — i.e. forged cross-resource API requests under the application's API key.Approach
src/shared/path-template.ts(with 11 unit tests): splits an OpenAPI path template like/orgs/{id}/users/{uid}into ordered literal/param segments. Now used by all four affected emitters instead of each rolling its own regex.src/{php,node,python,kotlin}/path-expression.ts. Rendering stays per-language because the grammars differ enough — PHP can't call functions inside"..."so it uses concatenation; Node uses template literals; Python uses f-strings withurllib.parse.quote(..., safe=""); Kotlin uses string templates with a runtime helper.buildPathExpression/buildPathExpr/buildPathStrduplicates from each emitter."connections/{$id}"'connections/' . rawurlencode($id)`connections/${id}``connections/${encodeURIComponent(id)}`f"connections/{id}"f"connections/{quote(str(id), safe='')}"(+ auto-injectedfrom urllib.parse import quote)"connections/${id}""connections/${encodePathSegment(id)}"(+ auto-importedcom.workos.common.http.encodePathSegment)Languages already safe (no changes)
Uri.EscapeDataStringurl.PathEscapeWorkOS::Util.encode_pathKotlin runtime dependency — outstanding
The kotlin emitter now imports
com.workos.common.http.encodePathSegment, which does not yet exist inworkos-kotlin. This is not blocking:workos-kotlin/mainbranch is structurally older than what the emitter generates (differentRequestConfigshape, differentbaseClient.request(...)signature). Kotlin regen is gated on the broader generator-cutover work anyway.URLEncoder.encodeis form-encoding (encodes space as+, leaves a few path-reserved chars), so the post-processing is required for RFC 3986 path-segment correctness.Companion PR
HttpClient::resolveUrl()that rejects../?/#/ CRLF. Lands independently of regen so existing PHP SDK users get protection immediately, and stays valuable after this emitter PR ships.Versioning
After regen, php / node / python release as patch (security fix). No public API surface changes — only the wire-format path is fixed. Kotlin regen is gated on the unrelated generator-cutover work.
Test plan
npx vitest run— 399 passed (45 files), 11 new tests intest/shared/path-template.test.tsnpx tsc --noEmit— cleannpx oxlint -D warnings— 0 warnings, 0 errorsnpx oxfmt --check .— clean🤖 Generated with Claude Code