16 Commits

Author SHA1 Message Date
James Anderson 4fe92e9d79 fix: apps with both app + pages router returning 404s (#252)
* fix: apps with both app + pages router returning 404s

* apply same fix for api routes

* test for pages dir route

* always filter x-middleware headers, and preserve others on redirect
2026-03-04 20:46:27 -06:00
Liu Xiaopai 5f593983bd fix(app-router): avoid React 19 dev Flight decode crash in SSR (#118)
* fix(app-router): avoid React 19 dev Flight decode crash in SSR (#50)

* fix(app-router): avoid React 19 dev Flight decode crash in SSR (#50)

* test(ecosystem): harden fixture cleanup and isolate better-auth signup data

* chore: address review nits on React 19 dev Flight decode fix

- Convert standalone JSDoc block to plain // comment to avoid confusion
  with the adjacent createRscEmbedTransform JSDoc
- Add note that VinextFlightRoot returns a thenable (not ReactNode),
  relying on React 19's internal thenable-as-child behavior
- Add comment noting the 'Switched to client rendering' assertion
  depends on React internal message text
- Clarify error.tsx fixture exists for realistic route structure, not
  because the error boundary fires during SSR
- Merge main into branch

---------

Co-authored-by: root <root@localhost.localdomain>
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-04 18:24:30 -06:00
milkboy2564 4ed5c89346 fix(rsc): emit actionable hint when non-plain object crosses server-client boundary (#250)
* fix(rsc): emit actionable hint when non-plain object crosses server-client boundary

  When a Server Component passes a class instance, ESM module namespace
  object, or null-prototype object as a prop to a Client Component,
  React's RSC serializer throws "Only plain objects, and a few built-ins,
  can be passed to Client Components from Server Components." In dev mode
  this error previously surfaced in the browser with no indication of
  which component or prop triggered it, making it hard to debug in large
  apps.

  Add a console.error in rscOnError that detects this specific error and
  prints an actionable guide explaining the four common causes, including
  the key ESM vs webpack difference: Vite produces true ESM module
  namespace objects (non-serializable), while Next.js webpack produces
  plain __esModule objects (serializable) — causing code to silently work
  in plain Next.js but correctly fail in vinext.

* test(rsc): add unit tests for rscOnError non-plain object dev hint

  Verify that the generated RSC entry code produced by generateRscEntry
  includes the dev-mode hint for issue #237. Tests cover:
  - Detection string for "Only plain objects" error is present
  - Hint is guarded by NODE_ENV !== "production"
  - Actionable guidance for module namespace objects is included
  - Actionable guidance for class instances is included
  - Existing digest return path for navigation errors is unaffected

* fix: escape newlines in rscOnError hint and add runtime tests

- Double-escape \n in the console.error hint strings so the generated
  JavaScript output contains valid string literals (the single-escaped
  \n was resolved to literal newlines by the template literal, producing
  unterminated strings that broke production builds).
- Add explicit 'return undefined' after the hint console.error block.
- Add runtime tests that extract rscOnError from the generated code via
  new Function() and verify: digest passthrough for navigation errors,
  console.error hint for RSC serialization errors, and undefined return
  for generic dev errors.

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-04 23:30:58 +00:00
Steve Faulkner 199297c4f4 fix: throw on missing middleware/proxy exports instead of failing open (#203)
* fix: throw on missing middleware/proxy exports instead of failing open

Middleware and proxy files that don't export the expected function
(e.g. misspelled export, wrong named export) were silently skipped,
letting requests through unprotected. This matches Next.js behavior
which throws a ProxyMissingExportError in the same scenario.

Changes:
- proxy.ts files now require 'proxy' or 'default' export
- middleware.ts files now require 'middleware' or 'default' export
- All 3 code paths updated (Pages dev, Pages prod, App Router)
- Added resolveMiddlewareHandler() with file-type-aware validation
- Ported test cases from Next.js proxy-missing-export test suite
- Updated AGENTS.md: added required step to search Next.js tests

Ported from Next.js: test/e2e/app-dir/proxy-missing-export/
https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/proxy-missing-export/proxy-missing-export.test.ts

* fix: update export precedence test to match Next.js behavior (named > default)

* fix: use ?? instead of || in generated middleware export resolution for consistency

Matches the shared resolveMiddlewareHandler() which uses nullish
coalescing. No behavioral difference since typeof check catches
all cases, but keeps the three code paths consistent.
2026-02-28 23:18:38 +00:00
Paul 584c2e9266 Fix Pages prod middleware named proxy export resolution (#188)
* fix pages prod middleware named proxy export

* remove security remediation report from PR
2026-02-28 22:26:37 +00:00
tomasgferreira 3d8aed9868 fix(config): respect has/missing conditions in next.config headers (#48)
* fix(config): respect has/missing conditions in next.config headers

Adds has/missing condition support to matchHeaders, wires request context
through dev/prod paths, updates NextHeader types, and adds unit + e2e tests
for header/cookie/query conditions.

* chore: add backward-compat comment for optional ctx in matchHeaders

Addresses review feedback: clarify that when no request context is
passed, has/missing conditions are silently skipped for backward
compatibility with existing callers.

* fix: wire has/missing conditions through redirects and rewrites in Pages Router dev

Pages Router dev was the only path missing has/missing support for
redirects and rewrites. The reqCtx was already being built for headers,
so hoist it and pass it to applyRedirects and applyRewrites too.

Also refactors applyRedirects and applyRewrites to delegate to the
shared matchRedirect/matchRewrite functions, eliminating duplicated
pattern-matching logic (same dedup pattern used for applyHeaders).

---------

Co-authored-by: tomoconstrutor <tomoconstrutor@gmail.com>
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-02-28 16:05:47 -06:00
nianyi(likai) 35757845d1 fix: eliminate react-dom/server.edge dual-import Rollup warning in SSR entry (#41)
* fix: use static import for renderToStaticMarkup to eliminate dual-import warning

The generated SSR entry imported renderToReadableStream statically from
react-dom/server.edge at the top level, but later used a dynamic
import() for renderToStaticMarkup from the same module. Rollup detects
this mixed static+dynamic import pattern and emits a warning for every
build:

  "react-dom/server.edge" is imported as both a static and dynamic
  import in the same module

Fix: add renderToStaticMarkup to the existing static import, removing
the redundant dynamic import(). The module was already loaded — the
dynamic import added no lazy-loading benefit since it runs
unconditionally during SSR.

* Remove comment on renderToStaticMarkup import

Removed comment about static import of renderToStaticMarkup.

* Remove unneccesary test

---------

Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
2026-02-27 07:13:10 +00:00
ask-bonk[bot] aff5d24449 fix(routing): allow hyphens in dynamic segment param names (#78)
* fix(routing): allow hyphens in dynamic segment param names

Catch-all and dynamic route params with hyphens (e.g. [[...sign-in]],
[auth-method]) caused 404 because the regex used \w+ which doesn't
match hyphens. Next.js allows hyphens in param directory names — this
is commonly used by Clerk ([[...sign-in]], [[...sign-up]]).

Changed all \w+ patterns to [\w-]+ across:
- app-router.ts (fileToAppRoute, discoverSlotSubRoutes, computeInterceptTarget)
- pages-router.ts (fileToRoute, patternToNextFormat)
- app-dev-server.ts (middleware matcher regex, config pattern matching)
- middleware.ts (pattern matching)

Closes #71

* fix(routing): allow hyphens in config matching and middleware-codegen param patterns

Addresses review feedback on PR #78: the same \w+ bug that was fixed in
routing/middleware also existed in the config pattern matching functions
(redirects, rewrites, headers) and the middleware-codegen tokenizer.

Updated all remaining param-related regex patterns to [\w-]+ across:
- index.ts: matchConfigPattern guard, tokenizer, catchAllMatch, applyHeaders
- config-matchers.ts: matchConfigPattern guard, tokenizer, catchAllMatch,
  escapeHeaderSource tokenizer
- app-dev-server.ts: __matchConfigPattern template (4 .replace() chains,
  catchAllMatch, __applyConfigHeaders)
- middleware-codegen.ts: generated matchMiddlewarePattern tokenizer

Subsumes PR #127 which fixed middleware-codegen.ts independently.

Co-authored-by: SeolJaeHyeok <milkboy2564@naver.com>

---------

Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com>
Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
Co-authored-by: SeolJaeHyeok <milkboy2564@naver.com>
2026-02-26 22:15:45 -08:00
Steve Faulkner 0c66d98fc8 fix: use decoded pathname for config matcher evaluation (#150)
Config matchers (redirects, rewrites, headers) now receive the decoded,
normalized pathname so that percent-encoded request paths like /%61bout
correctly match config rules defined for /about.

Both dev and prod servers rebuild the working URL from the decoded
pathname + original query string, ensuring all downstream consumers
operate on canonical paths.

Also adds unit and integration tests for percent-encoded path handling
in config matchers across App Router and Pages Router (dev + prod).
2026-02-27 02:15:50 +00:00
Steve Faulkner 65f78a4db6 fix: strip internal routing headers from dev server responses (#149)
Strip all x-middleware-* prefixed headers from responses in both the
App Router and Pages Router dev servers before sending to clients.
These headers are internal routing signals (x-middleware-next,
x-middleware-rewrite, x-middleware-override-headers, etc.) and should
not be visible to end users.

The prod server already stripped these correctly. This brings the dev
servers into parity.
2026-02-26 20:05:30 -06:00
Sunil Pai 8a618af9f6 fix: handle malformed percent-encoded URLs gracefully (#107)
Wrap all decodeURIComponent calls on user-controlled URL paths in
try/catch to return 400 Bad Request instead of throwing an uncaught
URIError that crashes the Node process.

A single request with a malformed percent-encoded path (e.g. /%E0%A4%A)
could terminate the entire server process. This affected all server
entry points: prod server (App + Pages Router), dev server middleware,
Cloudflare Worker entry, and generated RSC/middleware handlers.

Fixes:
- prod-server.ts: App Router and Pages Router request handlers
- app-router-entry.ts: Cloudflare Worker entry
- app-dev-server.ts: Generated RSC entry handler
- index.ts: Dev server connect middleware + generated middleware runner
  + generated NEXT_LOCALE cookie parser
- middleware.ts: Pages Router dev middleware runner

Includes 11 regression tests across app-router, pages-router, and
features test suites.
2026-02-26 10:52:57 +00:00
Sunil Pai fd01d7d8a6 fix: close security gaps in init.ts shell exec and app-dev-server external proxy (#106)
* fix: close security gaps in init.ts shell exec and app-dev-server external proxy

Gap 1 (GHSA-w7vc-6jjg-498h): migrate init.ts from execSync to execFileSync
with argument array, matching the pattern already applied in deploy.ts and
cli.ts. Inputs are hardcoded package names so practical risk is near zero,
but this eliminates the last instance of the flagged pattern.

Gap 2 (GHSA-pf96-3m7r-pv3j): harden the inline __proxyExternalRequest in
the generated RSC entry (app-dev-server.ts) to strip Cookie, Authorization,
x-api-key, proxy-authorization, and all x-middleware-* headers before
forwarding to external rewrite targets. Also adds a 30-second
AbortSignal.timeout. This brings the dev-server code path into parity with
the already-fixed proxyExternalRequest in config-matchers.ts.

Also fixes a missed GSP redirect path in dev-server.ts where backslash
normalization was not applied (GHSA-79rw-r8hc-qg8v — the GSSP path was
already committed).

Includes an integration test that verifies credential stripping end-to-end
via a mock HTTP server and a conditional external rewrite in next.config.ts.

* fix: unify isExternalUrl to detect all URL schemes and protocol-relative URLs

All four copies of isExternalUrl (config-matchers.ts, router.ts,
navigation.ts, app-dev-server.ts inline) now use the same RFC 3986
scheme regex: /^[a-z][a-z0-9+.-]*:/i.test(url) || url.startsWith('//')

Previously:
- config-matchers.ts and app-dev-server.ts only checked http:// and
  https:// (missing // protocol-relative)
- router.ts and navigation.ts checked http://, https://, and // but
  missed exotic schemes (data:, javascript:, blob:, ftp:, etc.)

Now all copies are consistent and detect any URL scheme per RFC 3986
plus protocol-relative URLs.

Adds test cases for exotic schemes, protocol-relative, hash-only, and
bare string inputs.

* fix: add shell: true to execFileSync for Windows .cmd file compat

On Windows, package managers (pnpm, npm, yarn) are .cmd batch files.
execFileSync without shell: true cannot execute them. Adding shell: true
lets the OS shell resolve .cmd files while still using the argument array
form for safer argument passing.
2026-02-26 10:33:08 +00:00
Sunil Pai 0e750f7934 Security hardening: redirect sanitization, header stripping, ALS isolation tests, and safer command execution (#105)
* Sanitize redirects/headers and use execFileSync

Replace execSync calls with execFileSync (pass argv arrays) to avoid shell interpolation and adjust package-install logic to split package manager and args. Sanitize redirect destinations in SSR paths to prevent protocol-relative open-redirects and use sanitizeDestinationLocal for Next.js redirects. Ensure middleware receives a cleaned pathname by stripping internal .rsc suffix before creating NextRequest and add a test for this behavior. Strip sensitive headers (cookie, authorization and x-middleware-*) from proxied external requests and add a test. Document cache key hazards for auth headers in fetch-cache, add a security note for dangerouslySetInnerHTML in head SSR, and set the Draft Mode cookie to include Secure in production (with tests for production/dev behavior).

* Rename middleware header to x-custom-middleware

Change middleware header name from x-middleware-test to x-custom-middleware across fixtures and tests, and clarify server comment about stripping x-middleware-* headers from responses (reserved for internal routing signals). Files updated: packages/vinext/src/server/prod-server.ts (comment tweak), tests/fixtures/pages-basic/middleware.ts (header set), and tests/pages-router.test.ts (expectations and test titles/comments). This ensures tests reflect the new public header name while preserving internal x-middleware-* semantics.

* Normalize leading slashes and strip headers

Normalize redirect/rewrite destinations by collapsing any leading slashes or backslashes to a single "/" to avoid protocol-relative redirects and handle browser-backslash behavior. Strip additional sensitive headers (x-api-key, proxy-authorization) from proxied requests and ensure all x-middleware-* internal headers are removed from responses (broadened from x-middleware-request-*). Update dev/server sanitization logic accordingly. Update tests and fixture middleware to use shortened x-mw-* header names and add tests covering backslash normalization.
2026-02-26 08:50:12 +00:00
Steve Faulkner 16988a5403 fix: add request validation to dev server (#98) 2026-02-25 22:51:16 -06:00
Steve Faulkner aca46417d9 fix: use Host header for server action origin check (#95) 2026-02-26 04:22:31 +00:00
Steve Faulkner 12fea722b6 Initial public release of vinext 2026-02-24 09:29:39 -06:00