The latest published minor release receives security fixes.
Email oss@nordvec.com with the details. Please include:
- The affected module (
partial-content, a store adapter, or a framework adapter) - A minimal reproduction (a failing request/response pair is ideal)
- The impact you believe it has (header injection, cache poisoning, path traversal, byte-splicing, DoS)
You will receive an acknowledgement within 72 hours. Please do not open a public issue for undisclosed vulnerabilities.
partial-content sits between attacker-controlled request headers and
storage-backend metadata. Defenses under test:
- Header injection (CWE-113) and header-write crashes: every
metadata-derived or caller-per-request response header value (ETag,
Last-Modified, Content-Type, digest, Cache-Control, and the signed-URL
Location) is stripped of ALL bytes outside RFC 9110 field-value grammar -- not just CRLF, but every control byte that NodewriteHead, undiciHeaders, and Workers reject by throwing, so a poisoned backend value can neither inject nor crash the process. ETag values are additionally held toetagcgrammar (interior DQUOTE removed) so a backend hash can never emit a structurally-broken entity-tag. Content-Disposition filenames AND the disposition type are fully sanitized (controls, bidi overrides, path components; the type is coerced toinline/attachment). - Path traversal / null bytes: the fs adapter resolves keys against a
fixed root and rejects
.., absolute paths (including Windows cross-driveD:\...keys), and\0. On Windows it also rejects reserved device names (NUL,CON,COM1, ...) and alternate-data-stream keys (file::$DATA), neither of which resolves to a file under the root. - Cache poisoning / byte splicing: strong-validator enforcement for
If-MatchandIf-Range, exact-match If-Range dates, pinned reads with one re-validation, and truthful Content-Range derived from the backend's actual response. - Range-amplification DoS (CWE-400): multi-range requests are served as
multipart/byteranges, but overlapping and adjacent ranges are coalesced first, and a request whose coalesced parts exceedmaxRanges(default 50) or already cover the whole representation degrades to a single full 200. A client cannot force a large or redundant multipart response by sending many tiny or overlapping ranges (the mitigation in Gonet/http.ServeContentand nginxmax_ranges). The multipartContent-Lengthis computed exactly, never chunked, so the response size is known and bounded up front. - MIME sniffing / encoding-sniffing XSS:
X-Content-Type-Options: nosniffon every success + error response (the200/206/404/502/503bodies and the bodyless412/416denials;304/302carry none),charset=utf-8enforcement on textual types,text/htmldeliberately absent from the built-in MIME map. Note thatsvgIS in the map (image/svg+xml) and SVG is active content: servedinlinefrom your own origin it can run same-origin script. For untrusted uploads, serve SVG asattachment(per-MIMEdispositionhook) or add a sandboxing CSP viasecurityHeaders. - Parser robustness: the range/Content-Range/disposition parsers are covered by seeded randomized invariant tests (never throw; bounds always valid; output always header-safe).
- Transparent-compression corruption: the http adapter requests
Accept-Encoding: identityand refuses (fails loudly) any response that still carries a non-identityContent-Encoding, so a decompressed body can never be served under compressed-size headers. - Conditional-date strictness: client-supplied
If-Modified-Since,If-Unmodified-Since, andIf-Rangedates are honored only in the three RFC 9110 HTTP-date formats; anything else (including ISO 8601) is ignored, as the spec mandates.
partial-content/http fetches whatever your url(key) returns; the origin
is part of YOUR trust boundary:
- Never interpolate untrusted input into
url()unencoded. A key like../other-bucket/secretchanges the fetched path. Encode path segments or validate keys before they reach the store. - Redirects error by default (
redirect: "error"). A serving layer whoseurl()may carry untrusted keys must not let a compromised or misconfigured origin bounce the store to internal endpoints such as cloud metadata IPs, so a 3xx is refused unless you opt in. Object-storage origins answer GET/HEAD with a direct 200, so the default costs nothing there. Setredirect: "follow"for origins that legitimately redirect, and pair it with a validatingfetchwhen keys are untrusted. - Network-level SSRF defenses are deliberately not built in (DNS pinning
and IP allowlists are deployment-specific). Supply them via the
fetchoption, which every request goes through.
Out of scope: authentication/authorization (gate access before calling the handler) and rate limiting (deploy-level concern).