Skip to content

fix(app-router): reject cyclic Flight payloads in server actions#798

Merged
southpolesteve merged 2 commits into
mainfrom
fix/server-actions-dos-guard
Apr 9, 2026
Merged

fix(app-router): reject cyclic Flight payloads in server actions#798
southpolesteve merged 2 commits into
mainfrom
fix/server-actions-dos-guard

Conversation

@southpolesteve
Copy link
Copy Markdown
Collaborator

Summary

  • bump the App Router dependency floor to react / react-dom / react-server-dom-webpack 19.2.5 and @vitejs/plugin-rsc 0.5.23, matching the upstream CVE-2026-23869 decoder updates
  • reject malformed cyclic Flight container-reference graphs before decodeReply, since vinext's App Router action path still reproduces the PoC without an early guard even after the upstream version bumps
  • add unit and integration coverage for the vendored decoder ordering, dependency floor, generated entry wiring, and cyclic multipart action payload rejection

Testing

  • vp test run tests/request-pipeline.test.ts
  • vp test run tests/init.test.ts
  • vp test run tests/app-router.test.ts -t "payload validation"
  • vp test run tests/app-router.test.ts -t "cyclic multipart server action payloads"

References

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 9, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@798

commit: 4abb58e

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 9, 2026

Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original

@southpolesteve
Copy link
Copy Markdown
Collaborator Author

/bigbonk review

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

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

Review

The security fix and dependency bumps look correct and well-tested. A few observations:

What's good:

  • The version bump to react/react-dom/react-server-dom-webpack 19.2.5 and @vitejs/plugin-rsc 0.5.23 correctly enforces the upstream CVE-2026-23869 decoder fix as a dependency floor
  • The validateServerActionPayload guard runs before decodeReply, which is exactly where it needs to be to prevent the PoC from triggering expensive deserialization
  • The vendored decoder regression tests in init.test.ts (expectConsumedBeforeInitialization) are a smart belt-and-suspenders approach — they'll catch if a future plugin-rsc update accidentally ships without the React fix
  • The cycle detection algorithm (standard DFS with gray/black coloring via stack/visited sets) is correct
  • Non-numeric FormData keys are properly skipped, so legitimate user form fields won't trigger false positives
  • The generated entry wiring test verifies ordering (validateServerActionPayload before decodeReply) at the template level
  • The integration test in app-router.test.ts uses AbortSignal.timeout(2_000) which is good defensive practice — if the guard somehow fails, the test won't hang
  • Dev/prod parity is maintained since the App Router prod server delegates to the built RSC entry

One minor observation (non-blocking):

collectRefs unconditionally adds entries to fieldRefs even when a numeric field contains zero container references (empty Set). This means a FormData payload with numeric fields containing no $Q/$W/$i patterns (e.g., body.set("0", '"hello"')) will still pass through the missing-reference and cycle-detection checks unnecessarily. It's harmless — empty sets have no refs to fail either check — but adding a if (refs.size > 0) guard before fieldRefs.set(fieldKey, refs) would skip the validation entirely for payloads that have no container references, matching the early fieldRefs.size === 0 bail-out more precisely. Not a correctness issue, just a minor clarity/performance point.

LGTM — good work catching that the upstream version bumps alone weren't sufficient for vinext's action path.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented Apr 9, 2026

Review posted and PR approved. Here's a summary of my findings:

Overall: LGTM — this is a well-structured security fix.

The PR does three things:

  1. Bumps the dependency floor for react/react-dom/react-server-dom-webpack to 19.2.5 and @vitejs/plugin-rsc to 0.5.23 to pull in the upstream CVE-2026-23869 decoder fixes
  2. Adds a validateServerActionPayload guard in request-pipeline.ts that rejects cyclic/malformed Flight container reference graphs before decodeReply runs — necessary because the upstream version bumps alone weren't sufficient for vinext's action path
  3. Adds regression tests at multiple layers: unit tests for the validator, integration tests against the dev server, generated-entry ordering tests, and vendored-decoder assertions that verify the React fix is present in plugin-rsc's bundled code

I noted one minor non-blocking observation about collectRefs adding empty sets to fieldRefs for numeric fields with no container references, but it's harmless since empty sets pass both the missing-reference and cycle checks.

github run

@southpolesteve southpolesteve merged commit f910e74 into main Apr 9, 2026
32 checks passed
@southpolesteve southpolesteve deleted the fix/server-actions-dos-guard branch April 9, 2026 03:11
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