Skip to content

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

Merged
jiashuoz merged 1 commit into
tokencanopy:mainfrom
AmirF194:fix/915-ts-generated-dot-segment-guard
Sep 1, 2026
Merged

fix(sdk): guard the generated TypeScript http.ts chokepoint against dot-segment path params#936
jiashuoz merged 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
@AmirF194

Copy link
Copy Markdown
Contributor Author

No rush, just checking in since this has been quiet for a week. Happy to rebase or split it further if that would make it easier to review.

@jiashuoz jiashuoz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changes and verified that the required CI checks pass.

@jiashuoz
jiashuoz merged commit a19f8a1 into tokencanopy:main Sep 1, 2026
24 checks passed
@AmirF194

AmirF194 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for picking this back up and merging it.

@AmirF194
AmirF194 deleted the fix/915-ts-generated-dot-segment-guard branch September 1, 2026 06:37
jiashuoz added a commit that referenced this pull request Sep 3, 2026
Cuts the three client packages against the diff since each one's last
PUBLISHED tag (ts-sdk-v5.8.0, cli-v2.5.0, python-v5.8.0) rather than since
the last server release, per the #935 post-mortem.

@e2a/sdk 5.9.0 (minor). #936 moves the dot-segment path guard down to the
generated RequestContext chokepoint, so every generated request is covered
instead of the ten hand-written wrappers 5.8.0 enumerated. Minor rather than
patch for the same reason 5.8.0 was: a path parameter of exactly "." or ".."
now throws instead of issuing a misdirected request. Generated model and
operation docs also pick up the outbound recipient-delivery metering
semantics and the messages_day / auth_unavailable vocabulary.

@e2a/cli 2.5.1 (patch). #947 stops the CSV parser leaking a raw carriage
return out of a quoted multi-line field, plus the #937 --send-at / review-hold
documentation correction.

e2a 5.8.1 (patch). Generated pydantic Field descriptions refreshed from the
current OpenAPI document; no name, signature, type, validation, or runtime
behavior differs from 5.8.0.

cli/package.json keeps its "@e2a/sdk": "^5.7.0" range: check-sdk-version-sync
compares majors, ^5.7.0 already resolves to 5.9.0 on a fresh install, and
leaving it avoids creating a window where cli-v2.5.1 is unsatisfiable.


Claude-Session: https://claude.ai/code/session_01AjfGxvXW6fNKWGFHuo68yX

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

2 participants