import { PROTECTED_PLUGIN_ROUTE_PREFIXES, canonicalizePathForSecurity, } from "../../security-path.js"; export type PluginRoutePathContext = { pathname: string; canonicalPath: string; candidates: string[]; malformedEncoding: boolean; decodePassLimitReached: boolean; rawNormalizedPath: string; }; function normalizeProtectedPrefix(prefix: string): string { const collapsed = prefix.toLowerCase().replace(/\/{2,}/g, "/"); if (collapsed.length <= 1) { return collapsed || "/"; } return collapsed.replace(/\/+$/, ""); } export function prefixMatchPath(pathname: string, prefix: string): boolean { return ( pathname === prefix || pathname.startsWith(`${prefix}/`) || pathname.startsWith(`${prefix}%`) ); } const NORMALIZED_PROTECTED_PLUGIN_ROUTE_PREFIXES = PROTECTED_PLUGIN_ROUTE_PREFIXES.map(normalizeProtectedPrefix); export function isProtectedPluginRoutePathFromContext(context: PluginRoutePathContext): boolean { if ( context.candidates.some((candidate) => NORMALIZED_PROTECTED_PLUGIN_ROUTE_PREFIXES.some((prefix) => prefixMatchPath(candidate, prefix), ), ) ) { return true; } if (!context.malformedEncoding) { return false; } return NORMALIZED_PROTECTED_PLUGIN_ROUTE_PREFIXES.some((prefix) => prefixMatchPath(context.rawNormalizedPath, prefix), ); } export function resolvePluginRoutePathContext(pathname: string): PluginRoutePathContext { const canonical = canonicalizePathForSecurity(pathname); return { pathname, canonicalPath: canonical.canonicalPath, candidates: canonical.candidates, malformedEncoding: canonical.malformedEncoding, decodePassLimitReached: canonical.decodePassLimitReached, rawNormalizedPath: canonical.rawNormalizedPath, }; }