Skip to content

GHSA-frvp-7c67-39w9: affected range <2.0.5 includes the 1.x line, but 1.19.15+ already blocks the %5C serve-static vector #387

Description

@dmang-dev

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:

  1. decodeURI('%5C') === '\\' — backslash is not a reserved URI character, so decodeURI (not only decodeURIComponent) decodes %5C to a literal backslash.
  2. 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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions