Description
encodeParam<T>() (ApiClientBaseGenerator.buildEncodeParam) is the sole mechanism used to
stringify every path parameter in every generated client:
public inline fun <reified T> encodeParam(value: T): String = Json.encodeToString(value).trim('"')
The result is interpolated directly into the URL path template with no further encoding, e.g.:
client.get("""${baseUrl}/resources/${encodeParam(resourceId)}/items/${encodeParam(itemId)}""")
JSON escaping and URL percent-encoding are different alphabets; .trim('"') only strips the outer
quotes and does nothing else. Concretely, for a path parameter value containing:
- a space → JSON doesn't escape it → a literal unencoded space lands in the URL (invalid, and
handled inconsistently across HTTP engines)
- a
/ → JSON doesn't escape it either → a literal / silently creates an unintended extra
path segment (path-injection risk, not just a display bug)
- a
" or \ → JSON escapes them as \"/\\, but .trim('"') only removes the outer quotes,
leaving a literal backslash in the URL
- a bare
% → left untouched, breaking percent-encoding parsing downstream
Root cause
encodeParam treats "produce a JSON token, then trim its quotes" as equivalent to "produce a
URL-safe path segment." It isn't — no path-safety encoding is ever applied.
Fix
Split the raw-value extraction from URL-encoding into two functions: encodeParam now extracts
the raw JSON-primitive content only (used for headers/query, which Ktor encodes itself), and a new
encodePathParam additionally applies Ktor's encodeURLPathPart() for path segments:
public inline fun <reified T> encodeParam(value: T): String =
Json.encodeToJsonElement(value).jsonPrimitive.content
public inline fun <reified T> encodePathParam(value: T): String =
encodeParam(value).encodeURLPathPart()
Acceptance criteria
Discovered in
Reproduced against justworks v0.2.5 / commit 088db52.
Description
encodeParam<T>()(ApiClientBaseGenerator.buildEncodeParam) is the sole mechanism used tostringify every path parameter in every generated client:
The result is interpolated directly into the URL path template with no further encoding, e.g.:
JSON escaping and URL percent-encoding are different alphabets;
.trim('"')only strips the outerquotes and does nothing else. Concretely, for a path parameter value containing:
handled inconsistently across HTTP engines)
/→ JSON doesn't escape it either → a literal/silently creates an unintended extrapath segment (path-injection risk, not just a display bug)
"or\→ JSON escapes them as\"/\\, but.trim('"')only removes the outer quotes,leaving a literal backslash in the URL
%→ left untouched, breaking percent-encoding parsing downstreamRoot cause
encodeParamtreats "produce a JSON token, then trim its quotes" as equivalent to "produce aURL-safe path segment." It isn't — no path-safety encoding is ever applied.
Fix
Split the raw-value extraction from URL-encoding into two functions:
encodeParamnow extractsthe raw JSON-primitive content only (used for headers/query, which Ktor encodes itself), and a new
encodePathParamadditionally applies Ktor'sencodeURLPathPart()for path segments:Acceptance criteria
/,", or%produces a correctly percent-encodedURL segment
Ktor's own encoding — no behavior change there
ApiClientBaseGeneratorTest,ClientGeneratorTest,IntegrationTest, andJustworksPluginFunctionalTestDiscovered in
Reproduced against justworks
v0.2.5/ commit088db52.