fix(tanstack): reject GET on /deco/invoke to close CSRF vector - #462
Open
0xcucumbersalad wants to merge 1 commit into
Open
fix(tanstack): reject GET on /deco/invoke to close CSRF vector#4620xcucumbersalad wants to merge 1 commit into
0xcucumbersalad wants to merge 1 commit into
Conversation
The TanStack admin route registered a GET handler on /deco/invoke that called handleInvoke. handleInvoke has no auth of its own and honors a `?props=` query string on GET (parseBody). A GET is a CORS "simple request" (no preflight), so a third-party page's <img src="https://site/deco/invoke/site/actions/checkout/updateCart.ts?props=..."> fired the mutating action with the victim's cookies attached — CSRF. The Next dispatcher (routeHandlers.ts) already rejects non-POST invoke for exactly this reason; the TanStack wiring diverged. Mirror it: GET now returns 405 (Allow: POST) and never reaches handleInvoke. POST is unchanged. Adds a CSRF regression to adminRoutes.test.ts: a forged cross-site GET with an action + props returns 405 and asserts the action handler is NEVER invoked; POST still dispatches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The TanStack admin route (
adminRoutes.ts) registered a GET handler on/deco/invokethat calledhandleInvoke:handleInvokehas no auth of its own and honors a?props=query string on GET (invoke.tsparseBody). A GET is a CORS simple request (no preflight), so it fires regardless of CORS policy.Impact — CSRF
A third-party page can trigger a mutating action with the victim's cookies attached:
No JS, no preflight — the victim just loads the attacker's page and the action runs as them.
The Next dispatcher already blocks this (
routeHandlers.ts:170-188405s non-POST invoke, with a comment naming this exact attack). The TanStack wiring diverged and kept a live GET.Fix
GET now returns 405 (
Allow: POST) and never reacheshandleInvoke, mirroring the Next dispatcher. POST is unchanged;withCors/OPTIONS preflight still work.Tests
adminRoutes.test.ts— new CSRF guard (2 tests):?props=returns 405 and asserts the action handler is never invoked (expect(handleInvoke).not.toHaveBeenCalled()— the load-bearing assertion: the mutation never fires);handleInvokecalled once, 200).Scope
Finding F3. Closes the no-preflight GET CSRF vector. Note
handleInvokeremains unauthenticated by design (loaders power the public storefront); a cross-site POST withContent-Type: application/jsonstill triggers a preflight — but is only actually blocked once the CORS policy stops reflecting arbitrary origins (F2, separate PR). Ship both for full coverage.Companion audit PRs: #459 (F5), #460 (F4), #461 (F6).
🤖 Generated with Claude Code
Summary by cubic
Blocked GET requests to
/deco/invokein the TanStack admin routes to close a CSRF vector; the route is now POST-only and mirrors the Next dispatcher. GET returns 405 withAllow: POST; POST behavior is unchanged./deco/invokewith a JSON 405 response and never callhandleInvoke.Written for commit 4420b6e. Summary will update on new commits.