mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
fb342f5ed6
* refactor(app-rsc): introduce app-rsc-request-normalization module The RSC entry handler inlined all URL parsing, pathname decoding, basePath checking, RSC detection, and header sanitization directly in its _handleRequest template. That made the logic impossible to unit-test, hard to audit for security, and duplicated normalizeMountedSlotsHeader in three places. Create server/app-rsc-request-normalization.ts to own the full normalization pipeline as a single, typed function (normalizeRscRequest). The function returns a discriminated Response | NormalizedRscRequest, encoding early exits (400/404) in the type rather than as throw-then-catch pairs. The ordering within the function is security-critical and documented step by step: 1. Parse URL 2. Protocol-relative guard (on raw pathname, before normalizePath collapses //) 3. Strict segment-wise percent-decode (malformed encoding -> 400) 4. normalizePath (collapse //, resolve . and .. segments) 5. basePath check + strip (missing prefix -> 404; /__vinext/ bypasses) 6/7. RSC detection and cleanPathname 8. Sanitize X-Vinext-Interception-Context (strip null bytes) 9. Normalize x-vinext-mounted-slots (dedup, sort) Consolidate normalizeMountedSlotsHeader: the function existed independently in isr-cache.ts and as a private copy in app-elements.ts. Both now import from the new module. isr-cache.ts re-exports it for callers already importing from there. 43 new behavior tests cover the security-sensitive paths: protocol-relative variants (literal, backslash, %5C, %2F), malformed percent sequences, basePath enforcement, %2F preservation, RSC detection by suffix and Accept header, interception context sanitization, and mounted-slots normalization. * refactor(app-rsc): wire normalizeRscRequest into the entry and callsites Replace the open-coded normalization block in the generated _handleRequest function with a single normalizeRscRequest call. The entry now delegates all security-sensitive preprocessing to the typed module rather than repeating it in a template string. Remove the local normalizeMountedSlotsHeader implementation from isr-cache.ts (was a standalone export) and remove the private copy from app-elements.ts. Both now import from app-rsc-request-normalization.ts. isr-cache.ts re-exports the function so existing callers that import from there are unaffected. Generated entry changes: - Add import for normalizeRscRequest from the new module - Remove guardProtocolRelativeUrl and stripBasePath from requestPipeline import - Remove normalizePathnameForRouteMatchStrict from routingUtils import - Remove normalizeMountedSlotsHeader from isrCache import - Replace ~20-line normalization block with 4-line call + destructure * refactor(app-rsc): move request lifecycle into typed handler The generated App Router RSC entry still owned request normalization, config routing, middleware handoff, public-file serving, action dispatch, fallback routing, and response finalization. That made lifecycle behavior hard to test directly and forced generator assertions to reach into runtime sequencing. The violated invariant was that codegen should describe the app shape while normal modules should implement behavior. This extracts the request lifecycle into createAppRscHandler, leaves the generated entry responsible for route-specific manifests and dispatch closures, and adds behavior tests for redirects, rewrites, public files, actions, route handlers, and fallback cleanup.