Security-audit housekeeping notes, commit abf4e87. Two low-impact correctness items.
(a) canonicalPathname does not collapse internal //, so the 308 redirect emits a double-slash Location
File: src/server.ts:56-58 (canonicalPathname) vs src/core/path.ts:8 (normalizePathname)
normalizePathname collapses internal //, but canonicalPathname does not. As a result the trailing-slash 308 redirect can emit a Location header that still contains double slashes, which then normalizes on the follow-up request → a redundant extra round-trip.
Suggested fix: collapse internal // in canonicalPathname (reuse normalizePathname's logic) so the 308 target is already fully canonical.
(b) Deferred-phase notFound() keeps HTTP 200
File: src/core/render.ts:231-233
The deferred phase ignores deeper.status, so a notFound() raised during the deferred phase still returns HTTP 200 with not-found content. This is acceptable (headers are already committed once the stream has flushed), but is a surprising behavior.
Suggested fix: add a code comment documenting that the deferred-phase status is intentionally left at 200 because headers are already committed, so future readers don't treat it as a bug.
Security-audit housekeeping notes, commit
abf4e87. Two low-impact correctness items.(a)
canonicalPathnamedoes not collapse internal//, so the 308 redirect emits a double-slashLocationFile:
src/server.ts:56-58(canonicalPathname) vssrc/core/path.ts:8(normalizePathname)normalizePathnamecollapses internal//, butcanonicalPathnamedoes not. As a result the trailing-slash 308 redirect can emit aLocationheader that still contains double slashes, which then normalizes on the follow-up request → a redundant extra round-trip.Suggested fix: collapse internal
//incanonicalPathname(reusenormalizePathname's logic) so the 308 target is already fully canonical.(b) Deferred-phase
notFound()keeps HTTP 200File:
src/core/render.ts:231-233The deferred phase ignores
deeper.status, so anotFound()raised during the deferred phase still returns HTTP 200 with not-found content. This is acceptable (headers are already committed once the stream has flushed), but is a surprising behavior.Suggested fix: add a code comment documenting that the deferred-phase status is intentionally left at 200 because headers are already committed, so future readers don't treat it as a bug.