Skip to content

fix(app-router): validate interception context identity - #3078

Merged
james-elicx merged 4 commits into
mainfrom
codex/fix-interception-context-dot-segments
Aug 25, 2026
Merged

fix(app-router): validate interception context identity#3078
james-elicx merged 4 commits into
mainfrom
codex/fix-interception-context-dot-segments

Conversation

@james-elicx

@james-elicx james-elicx commented Aug 25, 2026

Copy link
Copy Markdown
Member

Summary

  • validate client-provided App Router interception source paths before redirects and middleware
  • distinguish concrete source-route proof from compatible render fallbacks, keeping unverified variants out of CDN and ISR caches
  • revalidate interception proof after late rewrites and retain cacheability only for previously authorized sources
  • restrict exact interception selectors to read requests and propagate them consistently through probes and error rendering
  • add an isolated real-HTTP regression for a middleware-guarded interception source in development and production

Next.js parity

  • preserves the raw one-decode matching contract for encoded dynamic parameters
  • retains context-only fail-open rendering while preventing unverified shared-cache admission
  • applies through the shared App Router handler across dev, production, Node, and Workers
  • mirrors Next.js interception-plus-middleware routing while validating vinext's additional source-context header at its trust boundary

Validation

  • vp check on all changed files
  • targeted unit, integration, and App Router interception/middleware E2E coverage
  • the new real-HTTP regression fails on current origin/main in both dev and production (200 with guarded content exposed) and passes on this branch (400 with no guarded content)
  • vp run vinext#build
@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

Please limit this review to 7 minutes.

@pkg-pr-new

pkg-pr-new Bot commented Aug 25, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@3078
npm i https://pkg.pr.new/create-vinext-app@3078
npm i https://pkg.pr.new/@vinext/types@3078
npm i https://pkg.pr.new/vinext@3078

commit: 09e30a2

@github-actions

Copy link
Copy Markdown
Contributor
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
workers-cache preview production
web preview production
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared 09e30a2 against base 4146d69 using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 0 regressed · 6 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 142.3 KB 142.2 KB ⚫ -0.0%
Client entry size (gzip) vinext 129.5 KB 129.5 KB ⚫ -0.0%
Dev server cold start vinext 2.69 s 2.70 s ⚫ +0.4%
Production build time vinext 2.85 s 2.85 s ⚫ +0.1%
RSC entry closure size (gzip) vinext 116.2 KB 116.6 KB ⚫ +0.4%
Server bundle size (gzip) vinext 197.7 KB 198.1 KB ⚫ +0.2%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

setInterceptionResponseUncacheable(true);
return badRequestResponse();
}
const bypassInterceptionContextCache =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Revalidate interception proof after late rewrites

This proof is fixed before the afterFiles/fallback loops below. If the original target has a dynamic App match and a concrete intercept, it passes here; an afterFiles rewrite can then change cleanPathname to a different page, but bypassInterceptionContextCache remains false even though the generated findIntercept no longer matches that final target. The selector/context-bearing response can therefore be admitted to ISR/CDN without final target-specific proof (and context-only dynamic sources can fan out variants again). Recompute matchInterceptRoute after all late rewrites, rejecting an ID or forcing the context-only path to bypass caches before dispatch.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 19e3561. Late rewrite matches now invalidate the earlier proof immediately, and the final App target is revalidated before dispatch. Context-only mismatches bypass CDN/ISR caches; selector-bearing mismatches return 400. Added regressions for both paths.

@ask-bonk

ask-bonk Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Posted 1 actionable inline finding.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

Please review the cumulative diff, including the late-rewrite proof fix, and limit this review to 7 minutes.

setInterceptionResponseUncacheable(true);
return badRequestResponse();
}
bypassInterceptionContextCache = !hasVerifiedFinalInterceptionSource;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Authorize interception sources discovered after late rewrites

When the original dynamic target has no intercept for this context, interceptionSourceMatch is null, so the source-middleware pass above is skipped. An afterFiles/fallback rewrite can then land on a target for which finalInterceptionSourceMatch is concrete; this branch restores cacheability, and dispatchMatchedPage subsequently resolves that final intercept and renders its source route. Middleware protecting the source pathname never ran. Re-run source authorization for a newly selected final match (or keep it unverified/non-intercepting) before accepting this proof.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 09f6df8. Final proof after a late rewrite is accepted only when it is concrete and matches the route and params of the source already authorized before the rewrite. Newly discovered sources remain unverified/no-store. Added regressions for newly discovered and previously authorized sources.

@ask-bonk

ask-bonk Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Posted 1 actionable inline finding.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

Please review the complete updated diff and limit this review to 7 minutes.

@ask-bonk

ask-bonk Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

@james-elicx Bonk workflow was cancelled.

View workflow run · To retry, trigger Bonk again.

@ask-bonk

ask-bonk Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

@james-elicx Bonk workflow was cancelled.

View workflow run · To retry, trigger Bonk again.

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

Please review the complete diff at head 09e30a2, including the new real-HTTP regression, and limit this review to 7 minutes.

@james-elicx
james-elicx marked this pull request as ready for review August 25, 2026 12:51
@james-elicx
james-elicx merged commit c812639 into main Aug 25, 2026
71 checks passed
@james-elicx
james-elicx deleted the codex/fix-interception-context-dot-segments branch August 25, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant