Thanks for the quick serve-static security releases recently.
Summary
GHSA-frvp-7c67-39w9 ("Path traversal in serve-static on Windows via encoded backslash %5C") lists affected < 2.0.5, patched 2.0.5. That range covers the entire 1.x maintenance line, including the current latest-1 = 1.19.17 — but the v1.x branch appears to already neutralize the %5C vector. The result is that 1.x consumers get an advisory / npm audit hit with no in-range remediation (e.g. @modelcontextprotocol/sdk pins @hono/node-server at ^1.19.9, so those users can't move to 2.x without an override).
I wanted to flag it in case the range is broader than intended — happy to be corrected if I've missed a bypass.
Why 1.19.15+ looks already-protected
serveStatic decodes the request path before the traversal check (src/serve-static.ts on v1.x, lines 113-114):
filename = tryDecodeURI(c.req.path) // tryDecode(str, decodeURI)
if (/(?:^|[\/\\])\.{1,2}(?:$|[\/\\])|[\/\\]{2,}|\\/.test(filename)) {
// rejected -> not served
}
Two things combine to close the %5C vector on 1.x:
decodeURI('%5C') === '\\' — backslash is not a reserved URI character, so decodeURI (not only decodeURIComponent) decodes %5C to a literal backslash.
- The
|\\ alternative added to the regex in 1.19.15 then rejects any literal backslash.
Minimal check against the shipped 1.19.17 logic:
const tryDecodeURI = s => { try { return decodeURI(s) } catch { return s } };
const reject = f => /(?:^|[\/\\])\.{1,2}(?:$|[\/\\])|[\/\\]{2,}|\\/.test(f);
for (const p of ['/..%5Cwindows%5Csystem32', '/%5C%5Cserver%5Cshare', '/foo%5Cbar']) {
console.log(reject(tryDecodeURI(p)) ? 'BLOCKED' : 'ALLOWED', p);
}
// BLOCKED /..%5Cwindows%5Csystem32
// BLOCKED /%5C%5Cserver%5Cshare
// BLOCKED /foo%5Cbar
This serve-static hardening shipped in 1.19.15 (published 2026-07-24; appears to be commit 84cb2ee on v1.x), and src/serve-static.ts on v1.x still carries it at HEAD.
Request
Is the 1.x line intended to be in scope for GHSA-frvp-7c67-39w9, or was the advisory scoped to the 2.x release line?
If 1.19.15+ is considered fixed, would you consider narrowing the advisory's affected range — e.g. to >= 2.0.0, < 2.0.5, or adding a second affected range for 1.x with 1.19.15 as its patched version? That would clear a false-positive npm audit finding for the many consumers currently held on 1.x by the MCP SDK's ^1.19.9 constraint. If 1.x is intentionally out of support for this advisory, that's useful to know as well.
Thanks for maintaining this!
Thanks for the quick
serve-staticsecurity releases recently.Summary
GHSA-frvp-7c67-39w9 ("Path traversal in
serve-staticon Windows via encoded backslash%5C") lists affected< 2.0.5, patched2.0.5. That range covers the entire1.xmaintenance line, including the currentlatest-1= 1.19.17 — but thev1.xbranch appears to already neutralize the%5Cvector. The result is that 1.x consumers get an advisory /npm audithit with no in-range remediation (e.g.@modelcontextprotocol/sdkpins@hono/node-serverat^1.19.9, so those users can't move to 2.x without an override).I wanted to flag it in case the range is broader than intended — happy to be corrected if I've missed a bypass.
Why 1.19.15+ looks already-protected
serveStaticdecodes the request path before the traversal check (src/serve-static.tsonv1.x, lines 113-114):Two things combine to close the
%5Cvector on 1.x:decodeURI('%5C') === '\\'— backslash is not a reserved URI character, sodecodeURI(not onlydecodeURIComponent) decodes%5Cto a literal backslash.|\\alternative added to the regex in 1.19.15 then rejects any literal backslash.Minimal check against the shipped 1.19.17 logic:
This serve-static hardening shipped in 1.19.15 (published 2026-07-24; appears to be commit
84cb2eeonv1.x), andsrc/serve-static.tsonv1.xstill carries it at HEAD.Request
Is the
1.xline intended to be in scope for GHSA-frvp-7c67-39w9, or was the advisory scoped to the 2.x release line?If 1.19.15+ is considered fixed, would you consider narrowing the advisory's affected range — e.g. to
>= 2.0.0, < 2.0.5, or adding a second affected range for1.xwith1.19.15as its patched version? That would clear a false-positivenpm auditfinding for the many consumers currently held on 1.x by the MCP SDK's^1.19.9constraint. If 1.x is intentionally out of support for this advisory, that's useful to know as well.Thanks for maintaining this!