Skip to content

encodeParam() quote-trim instead of URL-encoding: path parameters aren't properly escaped #108

Description

@mfabisiak

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

  • A path parameter containing a space, /, ", or % produces a correctly percent-encoded
    URL segment
  • Header and query parameters continue to use the raw (non-URL-encoded) value, relying on
    Ktor's own encoding — no behavior change there
  • Covered by regression tests in ApiClientBaseGeneratorTest, ClientGeneratorTest,
    IntegrationTest, and JustworksPluginFunctionalTest

Discovered in

Reproduced against justworks v0.2.5 / commit 088db52.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions