VDB
KO
LOW 2.5

GHSA-g7r4-m6w7-qqqr

esbuild allows arbitrary file read when running the development server on Windows

Details

### 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

Are you affected?

Enter the version of the package you're using.

Affected packages

npm / esbuild
Introduced in: 0.27.3 Fixed in: 0.28.1
Fix npm install esbuild@0.28.1

References