Skip to content

fix(sdk): guard the generated TypeScript http.ts chokepoint against dot-segment path params - #936

Open
AmirF194 wants to merge 1 commit into
tokencanopy:mainfrom
AmirF194:fix/915-ts-generated-dot-segment-guard
Open

fix(sdk): guard the generated TypeScript http.ts chokepoint against dot-segment path params#936
AmirF194 wants to merge 1 commit into
tokencanopy:mainfrom
AmirF194:fix/915-ts-generated-dot-segment-guard

Conversation

@AmirF194

Copy link
Copy Markdown
Contributor

Summary

#915 (filed during review of #909) shows that the per-call-site dot-segment
guard in the ergonomic wrapper classes never runs if a caller uses a
generated *Api class directly, or MCP/CLI code that goes straight through
the generated layer: encodeURIComponent does not escape ., so a value
of .. survives into the templated URL string, and new URL() then
collapses it before either Middleware.pre() or RetryHttpLibrary.send()
ever see the request. RequestContext's constructor (and its unused
setUrl twin) is the only place that still holds the raw, un-collapsed
string, so that is where the guard has to live.

http.ts is regenerated by make generate and is not in
.openapi-generator-ignore, so a hand edit would not survive the next
codegen run. This PR adds a post-processing step
(scripts/guard-dot-segment-path.py, wired into generate-oag.sh) that
injects the guard into the generated file, mirroring the existing
guard-optional-header-params.py pattern for the same reason: a static
audit (the new dot-segment-path-guard.test.ts) pins the emitted shape so
a future generator upgrade fails loudly instead of silently re-blessing an
unguarded chokepoint.

Scope: this is the TypeScript half of #915's generic guard (the Python
half shipped in #929, Refs #915). #915 also asks for a corrected,
per-path-parameter-position router regression test (the "priority 2"
section) as a Go-side follow-up; that is not part of this PR, so this stays
Refs #915 too, not Fixes #915.

Deleted the client-surface checklist below: no API surface, generated
types, or other client changed; this is a TypeScript-SDK-only hardening fix.

Operational risk

None. The guard only rejects a path segment of exactly . or ..; no
legitimate email, id, or domain value is a bare ./.. segment, and the
query string is excluded from the check.

Test plan

  • New dot-segment-path-guard.test.ts: constructs RequestContext and a
    generated DomainsApiRequestFactory directly, confirming domain=".."
    throws before a request is built while an ordinary domain value still
    builds the expected URL. A static audit over the committed http.ts
    pins that both new URL(ensureAbsoluteUrl(url)) call sites stay guarded.
  • New scripts/test_guard_dot_segment_path.py: unit tests the
    post-processing script directly (guards both call sites, idempotent
    re-run, raises instead of silently no-opping if the generator's emitted
    shape ever drifts from 2 call sites).
  • make generate-sdk-check (the repo's generated-code freshness gate,
    matching its own CI job: Go 1.26, Node 22, Python 3.12): regenerating
    from the spec reproduces this PR's http.ts diff exactly, git diff --exit-code clean.
  • npm run test:coverage --workspace @e2a/sdk: typecheck, full test/v1
    unit suite (269 tests, including the 7 new ones), and type tests all
    green.
  • Not independently run: the live-server ts-contract CI job (needs a
    Postgres-backed Go server). Its fixtures use realistic domain/email
    values, none of which are a bare ./.. path segment, so I don't
    expect it to interact with this change, but I have not executed it.

…ot-segment path params

RequestContext's constructor hands its templated url straight to new URL(),
which silently collapses a "." or ".." path segment before Middleware.pre()
or RetryHttpLibrary.send() ever see the request, so a caller-controlled
value like a domain of ".." retargets a call at a different, larger-scoped
resource. tokencanopy#929 closed this for the Python SDK's single param_serialize
chokepoint; the TypeScript generated layer had no equivalent, and tokencanopy#915
explains why the two proposed client-side hooks can't see the collapse in
time.

Since http.ts is regenerated by make generate and isn't in
.openapi-generator-ignore, the guard is injected by a new codegen
post-processing step (scripts/guard-dot-segment-path.py, wired into
generate-oag.sh) rather than hand-edited, mirroring the existing
guard-optional-header-params.py pattern.

Fixes tokencanopy#915
@AmirF194
AmirF194 requested a review from jiashuoz as a code owner August 24, 2026 05:45
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.

1 participant