Skip to content

map: keep the request namespace through renamed destructuring - #128

Merged
patchstackdave merged 1 commit into
mainfrom
fix/map-aliased-request-namespace
Aug 13, 2026
Merged

map: keep the request namespace through renamed destructuring#128
patchstackdave merged 1 commit into
mainfrom
fix/map-aliased-request-namespace

Conversation

@patchstackdave

@patchstackdave patchstackdave commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Found while splitting extract.ts — reported there, fixed here, because that PR's contract was zero behaviour change. Now rebased onto main; this branch contains only the fix.

The bug

The request namespace decides an input's runtime coordinate. It was recorded as a Set of local names and then compared against the literal strings 'query' / 'params' — so with a renamed destructuring the local is the alias, the comparison fails, and the namespace is silently discarded:

({ query })     => query.doc     get.doc     // correct
({ query: q })  => q.doc         post.doc    // WRONG — can never match a query-string attack
({ params: p }) => p.id          post.id     // WORSE — a coordinate for a route param

The third line is the serious one. Route params aren't exposed by the runtime resolver at all — that's precisely why runtimeCoordinate returns null for them. Aliasing bypassed that guard and would hand a rule compiler an address the engine can never resolve. Same failure class as attributing a sink by name: a coordinate that looks plausible and quietly does nothing.

The fix

sourceNames becomes a Map from local name → the namespace it was bound from, so an alias resolves to its true source. An aliased request-body read (const b = await req.json()) now also keeps its precise source (json-body / form-body) instead of collapsing to a generic body.

Verified

shape before after
({ query }) get.doc get.doc
({ query: q }) post.doc get.doc
({ params: p }) post.id null (+ no candidate)
({ body: b }) post.file post.file
({ query: q }) → const { doc } = q post.doc get.doc

Plus the consequence that matters: the aliased route param produces no candidate, while the four addressable inputs still compile candidates. 851 tests (80 files), typecheck clean.

@coderbuds

coderbuds Bot commented Aug 13, 2026

Copy link
Copy Markdown

Correctly preserves request namespace through renamed destructuring.

🎯 Quality: 85% Excellent · 📦 Size: Oversized — strongly consider breaking this down

🛡️ Standards: no pre-flight fit check ran for this change — wire assess-change-fit into your coding agents to catch size before opening.

📈 This month: Your 59th PR — above team average · Averaging Good

See how your team is trending →

@patchstackdave

Copy link
Copy Markdown
Contributor Author

/review

Found while splitting extract.ts. The request namespace decides an input's runtime
coordinate, but it was recorded as a SET of local names and then compared against the
literal strings 'query'/'params'. With a renamed destructuring the local is the alias, so
the comparison failed and the namespace was silently discarded:

    ({ query })    => query.doc   ->  get.doc    (correct)
    ({ query: q }) => q.doc       ->  post.doc   (WRONG: never matches a query-string attack)
    ({ params: p }) => p.id       ->  post.id    (WORSE: a coordinate for a route param)

The second case is the dangerous one: route params are not exposed by the runtime resolver at
all, which is exactly why runtimeCoordinate returns null for them — and aliasing bypassed that
guard, handing a rule compiler an address the engine can never resolve. This is the same
failure class as attributing a sink by name: a coordinate that looks plausible and quietly
does nothing.

`sourceNames` is now a Map from local name to the namespace it was bound from, so an alias
resolves to its true source. An aliased request-body read (`const b = await req.json()`) also
keeps its precise source (json-body / form-body) instead of collapsing to a generic body.

Covered for all five shapes plus the candidate consequence: the aliased route param yields no
coordinate and therefore no candidate, while the four addressable ones still compile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@patchstackdave
patchstackdave force-pushed the fix/map-aliased-request-namespace branch from e9d0404 to 8af63b5 Compare August 13, 2026 14:25
@patchstackdave
patchstackdave merged commit fe8710f into main Aug 13, 2026
5 checks passed
@patchstackdave
patchstackdave deleted the fix/map-aliased-request-namespace branch August 13, 2026 14:28
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