Track vulnerabilities like this one.
Sign up free to get alerted when software you use is affected.
2.5
esbuild allows arbitrary file read on Windows development server
published 4 months ago
Summary
A vulnerability in esbuild's development server on Windows allows attackers to access files outside the intended directory. This is because the server does not correctly handle Windows path separators. To fix this, update to a version of esbuild that addresses this issue.
What to do
- Update GitHub Actions esbuild to version 0.28.1.
Affected software
| Ecosystem | Vendor | Product | Affected versions |
|---|---|---|---|
| npm | GitHub Actions | esbuild |
>= 0.27.3, < 0.28.1 Fix: upgrade to 0.28.1
|
Original advisory text
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
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
Internet-facing
60 days
Internal
At next upgrade
- Not known to be exploited
- Needs hands-on effort to exploit (estimated)
- Gives an attacker partial control (estimated)
Type
CWE-22Path Traversal
Timeline
Published12 Jun 2026
Updated12 Jun 2026
First seen12 Jun 2026
Sources
GHSA-g7r4-m6w7-qqqr · GHSA
Track software like this
Free during beta