22 Commits

Author SHA1 Message Date
MD YUNUS b89afe0736 fix: wire up RouterContext.Provider in Pages Router (#239) (#249)
Add wrapWithRouterContext() function to the next/router shim that wraps
any React element in a <RouterContext.Provider>. This is the root-cause
fix: it builds the router value object directly from the SSR context
(server) or window.location + Router singleton (client) — no hooks, no
duplicate state, no extra component.

This provides a single source of truth for the router context value,
matching how Next.js provides RouterContext at the framework level.

Wrapping sites:
- dev-server.ts: main render, ISR re-render, error page render
- index.ts virtual module: production renderPage and ISR re-render
- Hydration script: client-side hydration entry
- navigateClient: client-side navigation re-renders

Tests:
- useRouter returns null without provider (default/App Router)
- useRouter returns full router when wrapWithRouterContext wraps tree
- useRouter reflects SSR context (pathname, query, asPath)
- wrapWithRouterContext is exported from next/router

Closes #239
2026-03-04 22:12:29 +00:00
harris.dev df3b2c3e37 fix(shims): add missing URL setters to NextURL (#125)
* fix(shims): add missing URL setters to NextURL

The NextURL class only had setters for pathname, search, and hash, leaving
port, host, hostname, protocol, href, username, and password as getter-only.

Libraries like next-intl's getAlternateLinksHeaderValue clone nextUrl then set
.port and .host to normalise the URL for alternate-link headers. In Cloudflare
Workers, x-forwarded-host is always present, so this code path is always
triggered, producing:

  TypeError: Cannot set property port of [object Object] which has only a getter

Add setters for all writable URL properties so NextURL behaves identically to
the platform URL class.

Fixes runtime middleware errors when using next-intl in production on Workers.

* revert build artifact to main (remove local machine paths)

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-04 17:57:40 +00:00
Robert Soriano 190c1c50e3 fix: Add next/compat/router shim (#224)
* feat: Add next/compat/router shim

* chore: update doc

* Apply suggestion from @ask-bonk[bot]

Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>

---------

Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>
2026-03-03 10:26:23 -06:00
Steve Faulkner 5ece95e9f8 fix: handle SVGs in image optimization to match Next.js behavior (#205) (#236)
SVGs are auto-skipped on the client side (set unoptimized=true) unless
dangerouslyAllowSVG is enabled in next.config. When dangerouslyAllowSVG
is true, SVGs that reach the server are passed through without
transformation, with security headers (CSP, Content-Disposition)
applied.

Config options added to NextConfig.images:
- dangerouslyAllowSVG (default: false)
- contentDispositionType (default: "inline")
- contentSecurityPolicy (default: "script-src 'none'; frame-src 'none'; sandbox;")

Co-authored-by: Dhruv Sharma <dhruvcoding67@gmail.com>
2026-03-03 08:53:36 -06:00
harris.dev 74f92ac910 fix(shims): copy request headers into mutable Headers in headersContextFromRequest (#128)
In Cloudflare Workers, Request.headers is immutable per the Fetch API spec.
headersContextFromRequest was storing the original request.headers reference
directly in HeadersContext, so when applyMiddlewareRequestHeaders later called
ctx.headers.set() (to propagate middleware-modified headers like x-request-url
into the server component context), Workers threw:

  TypeError: Can't modify immutable headers.

Fix by wrapping with new Headers(request.headers), which creates an independent
mutable copy while preserving all header values.
2026-03-01 19:44:37 -06:00
Jökull Sólberg Auðunsson f56b14d474 fix: matchConfigPattern incorrectly matches :param with literal suffix (#191)
Patterns like `/:slug.md` were falling through to the simple segment
matcher, which treated `slug.md` as the entire parameter name and
matched any single-segment path (including `/`). This caused rewrites
like `{ source: "/:slug.md", destination: "/api/markdown/:slug" }` to
rewrite every page request, resulting in 404s.

Add `/:[\w-]+\./.test(pattern)` to the condition that triggers the
regex-based matcher, which already tokenizes `:slug` + `.` + `md`
correctly.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 19:27:15 -06:00
Steve Faulkner 512bf0dc71 fix: make RequestContext required on matchHeaders/matchRedirect/matchRewrite (#213)
Next.js always has request context when evaluating has/missing
conditions on headers, redirects, and rewrites. There is no code path
where these are evaluated without a request.

Making ctx required eliminates ambiguity about what happens when
context is missing, and surfaces any future call sites that forget
to pass it as compile-time errors instead of silent behavioral bugs.

Also fixes deploy.ts matchHeaders call that was missing reqCtx.

Co-authored-by: Eray <erayackgoz@gmail.com>
2026-03-01 12:03:02 -06: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
Fred K. Schott 7b050d2064 chore: suppress no-implied-eval warnings in codegen tests (#156)
These tests intentionally use new Function() to evaluate generated
middleware codegen output. Add eslint-disable-next-line comments to
fix --deny-warnings CI failures.
2026-02-27 08:18:26 +00: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 6fb62b6017 fix: decode request pathname once at entry point for consistent routing (#148)
Ensures the pathname is decoded exactly once (via decodeURIComponent +
normalizePath) at the request entry point, and all downstream consumers
(middleware, config matchers, route matching) receive the same canonical
path. Previously, some code paths decoded the pathname a second time,
which could cause inconsistent matching between middleware and routing.

Also removes redundant decodeURIComponent calls from matchRoute and
matchConfigPattern catch-all branches that are no longer needed since
the caller now guarantees a pre-decoded pathname.
2026-02-26 20:04:40 -06: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
Dane 98b243493f fix: resolve CodeQL security alerts (ReDoS, incomplete sanitization, bad code gen) (#87)
Refactor chained .replace() to single-pass tokenizers in pattern matching:
- matchConfigPattern in config-matchers.ts and index.ts
- escapeHeaderSource (new) replaces inline chained escaping in matchHeaders/applyHeaders
- matchMiddlewarePattern in prod server entry template
- matchPattern in middleware.ts

Also:
- escapeHeaderSource correctly handles :param(constraint) patterns
- Fix decodeURIComponent parity gap in index.ts matchConfigPattern
- Fix redundant dot in middleware.ts tokenizer char class
- Add lgtm suppression comments for false positives in test files
- Add unit tests for escapeHeaderSource
2026-02-26 00:18:46 -06:00
Steve Faulkner 83495447ae fix: normalize request pathname before middleware and route matching (#102)
- Add normalizePath utility that collapses consecutive slashes,
  resolves . and .. segments, and ensures a leading slash
- Apply decodeURIComponent + normalizePath before middleware matching
  and route matching in all entry points (dev server, prod server,
  Cloudflare Worker entry, codegen entries)
- Extract shared middleware codegen module (middleware-codegen.ts) as
  single source of truth for pattern matching logic, replacing
  hand-copied inline implementations in index.ts and app-dev-server.ts
- Fix default middleware matcher to return true when no matcher is
  configured, matching Next.js behavior
- Fix regex pattern corruption in middleware matcher: patterns containing
  parentheses or backslashes are now passed to RegExp directly without
  dot-escaping
2026-02-26 05:39:36 +00:00
Steve Faulkner 6986e1017f fix: validate image optimization content types and width bounds (#101)
- Add Content-Security-Policy and X-Content-Type-Options headers to image responses
- Validate Content-Type allowlist (reject SVG, HTML, non-image types)
- Bound width parameter to configured deviceSizes/imageSizes with 3840px max
- Add deviceSizes and imageSizes to NextConfig type
- Apply to all image paths: Workers, Node.js prod server (App + Pages Router)
- Fix benchmarks limit param NaN handling
2026-02-26 05:08:44 +00:00
Steve Faulkner 16988a5403 fix: add request validation to dev server (#98) 2026-02-25 22:51:16 -06:00
Steve Faulkner af485aec24 fix: validate module paths during client-side navigation (#94) 2026-02-26 04:19:43 +00:00
Steve Faulkner 3ff59fa6d4 fix: sanitize redirect destinations and digest parsing (#92) 2026-02-26 04:14:56 +00:00
Steve Faulkner 12fea722b6 Initial public release of vinext 2026-02-24 09:29:39 -06:00