Jun 15, 2026·7 min read·18 visits
The esbuild local development server on Windows is vulnerable to arbitrary file read due to a path traversal flaw caused by improper backslash normalization.
Improper validation of backslash character separators in esbuild's local development server allows path traversal on Windows systems.
The vulnerability designated as GHSA-G7R4-M6W7-QQQR exposes a path traversal flaw in the local development server of esbuild, a widely adopted JavaScript bundler and minifier. The flaw is specifically present when the development server is run on the Windows operating system. It allows an unauthenticated remote attacker to retrieve arbitrary files from the host filesystem that are accessible to the user running the esbuild process.
In modern web development workflows, developers utilize local servers to test, hot-reload, and debug their front-end applications. The esbuild tool provides a built-in development server via its API (the esbuild.serve() function) to host compiled assets and serve static directories. Because this server interacts directly with the local file system to locate and deliver files, rigorous input validation of HTTP request paths is essential to maintain the security boundary.
This vulnerability arises because the server fails to properly sanitize Windows-style backslash (\) path separators when checking if a requested asset lies within the designated root directory. Consequently, requests designed to traverse directory structures bypass the internal safety checks and map directly onto the underlying Windows file system APIs, which treat both forward slashes and backslashes as path separators.
To validate request paths, the esbuild development server relies on Go's standard library path package, specifically the path.Clean() function. This function is designed to normalize path strings by resolving relative segments (such as ., ..), removing duplicate separators, and cleaning trailing slashes. This normalized path is then concatenated with the server's designated serve directory to locate the requested file on disk.
The fundamental limitation of Go's path package is that it is designed strictly for Unix-style paths. It treats the forward slash (/) as the only valid path separator. When presented with a backslash (\), path.Clean() does not recognize it as a path boundary but treats it as a standard literal character. Therefore, path traversal payloads containing backslashes, such as /..\..\..\Windows\win.ini, bypass the normalization routine entirely.
Once the unsanitized path bypasses the Go path cleaning routine, the application passes the string to OS-level file system calls. On the Windows operating system, filesystem APIs natively accept both forward slashes and backslashes as valid directory separators. When the system API receives the unresolved traversal elements (..\), it moves up the directory tree relative to the serving root, culminating in access to absolute file locations across the entire drive volume.
The patch introduced in version 0.28.1 directly addresses the root cause by preventing any path containing a backslash from being handled by the server. Because the local development server only needs to serve files based on standard HTTP URL paths (which strictly use forward slashes), backslash characters are inherently invalid in legitimate incoming request paths.
The logic changes in the HTTP request handler filter out invalid characters before passing the path string to the cleanup functions. In the vulnerable version, the server accepted any request path and passed it directly to path.Clean:
// Vulnerable logic flow (representation)
func serveFile(w http.ResponseWriter, r *http.Request) {
// Request path is taken directly from the URL
reqPath := r.URL.Path
// path.Clean only cleans forward slashes. Backslashes remain untouched.
cleanedPath := path.Clean(reqPath)
// On Windows, os.Open resolves backslashes, triggering traversal
file, err := os.Open(filepath.Join(serveDir, cleanedPath))
// ...
}The remediated code implements a strict check that inspects the request path for the presence of backslash characters before executing any filesystem interactions. If a backslash is detected, the server immediately rejects the request with a standard bad request error or a localized 400/404 response. This simple but highly effective validation ensures that the Windows filesystem API is never forced to parse unsafe OS-specific separators:
// Patched logic flow (representation)
func serveFile(w http.ResponseWriter, r *http.Request) {
reqPath := r.URL.Path
// Explicit rejection of backslash characters to prevent Windows path traversal
if strings.Contains(reqPath, "\\") {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Safe to clean and join as only forward slashes are present
cleanedPath := path.Clean(reqPath)
file, err := os.Open(filepath.Join(serveDir, cleanedPath))
// ...
}This remediation strategy is robust because it creates an absolute barrier against Windows path-delimiter trickery. By validating the input path at the boundary of the HTTP server, esbuild avoids having to implement complex, platform-dependent sanitization routines deep inside its core file-handling layers.
Exploitation of this vulnerability is straightforward and requires no active authentication. An attacker must construct an HTTP GET request targeting the running esbuild development server. The request URL must contain directory traversal sequences utilizing Windows backslash separators to escape the restricted directory.
For example, if the esbuild server is serving files from C:\projects\my-app\www, a standard request for /index.html resolves safely. However, a crafted request containing backslashes bypasses the path.Clean check and maps outside the folder structure:
GET /..\..\..\..\..\..\Windows\win.ini HTTP/1.1
Host: localhost:8000
Connection: closeWhen the esbuild server processes this request, the path is checked via the Unix-only path.Clean(). Finding no forward slashes in the traversal sequences, the path resolver treats the sequence ..\..\ as a single literal folder name, returning it as-is. When the file retrieval layer joins this with the serve directory (C:\projects\my-app\www\..\..\..\..\..\..\Windows\win.ini), the Windows operating system normalizes the sequence to C:\Windows\win.ini and returns the file contents to the client.
The security impact of GHSA-G7R4-M6W7-QQQR is rated as High, with a CVSS v3.1 base score of 7.5. Although the vulnerability does not lead to direct code execution or file modification, the confidentiality compromise is severe. An attacker can read any configuration files, application source code, local databases, or operating system configuration files that the running developer's process has permissions to read.
In typical local development workflows, esbuild runs with the privileges of the active developer. This means that any file accessible to the developer—including private SSH keys, .env files containing production credentials, browser session cookies, and sensitive documents—can be extracted by a remote actor who can route requests to the development server.
Furthermore, because the local development server may bind to all interfaces (0.0.0.0) by default or because of manual developer configuration, the server is frequently exposed to other hosts on the same local area network (LAN). This local exposure increases the threat vector from a purely local attacker to any compromised machine or malicious actor residing on the same subnet.
To remediate the vulnerability, users and developers must upgrade their esbuild installation to version 0.28.1 or later. This update is available through standard package managers such as npm or Deno's module resolution system. After upgrading, the server automatically rejects all path requests containing backslashes, closing the traversal vector entirely.
If upgrading immediately is not feasible, developers should implement defensive configuration measures. The most effective workaround is to ensure that the development server binds strictly to the local loopback interface (127.0.0.1 or ::1) rather than the wildcard interface (0.0.0.0). This prevents other devices on the same network from initiating connections to the vulnerable server.
Additionally, implementing a reverse proxy or utilizing firewall rules to block inbound TCP traffic to the esbuild server port can prevent unauthorized access. Organizations should also encourage developers to run development tools within containerized or sandboxed environments to isolate sensitive host directories from any potentially exposed development processes.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
esbuild evanw | < 0.28.1 | 0.28.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network (HTTP) |
| CVSS v3.1 Score | 7.5 (High) |
| Exploit Status | PoC Available |
| Patch Version | 0.28.1 |
| Affected OS | Windows |
The software uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize sequences such as '..' that can resolve to a location outside of the directory.
Flyto2 Core (flyto-core) prior to version 2.26.7 did not utilize its centralized SSRF validation mechanism ('validate_url_with_env_config') across multiple HTTP-emitting modules. This oversight allowed low-privileged users executing automated workflows to perform Server-Side Request Forgery (SSRF) attacks against internal endpoints, loopback interfaces, and cloud provider metadata services.
An SSRF vulnerability exists in Flyto2 Core due to improper validation of intermediate HTTP redirect hops. While the initial request target is validated against an SSRF protection policy, the HTTP client library (aiohttp) transparently follows 30x redirects to local, internal, or cloud metadata endpoints without application-level revalidation.
A logic vulnerability exists in @dynatrace-oss/dynatrace-mcp-server prior to version 1.8.7. The create_dynatrace_notebook tool lacks a human-approval gate, allowing an attacker to exploit indirect prompt injection to force the underlying LLM client to create persistent Dynatrace notebooks without the operator's consent.
A critical authentication and authorization bypass vulnerability in the Quarkus Java framework exists due to a parser differential mismatch between the HTTP security policy layer and downstream handlers. By leveraging encoded reserved characters such as semicolons, slashes, and backslashes, attackers can bypass configured path-based security policies to gain unauthorized access to secure administrative endpoints and static resources.
A critical code injection vulnerability exists in @aws/agentcore CLI (AWS AgentCore CLI) during the Bedrock Agent import lifecycle. An authenticated remote attacker with permissions to configure Bedrock collaborator attributes can inject python code by embedding triple-double-quotes (""") inside the collaborationInstruction metadata field. The CLI formats this metadata directly into a Python docstring in a generated main.py file without adequate escaping, leading to arbitrary code execution when the imported agent is run or deployed.
GHSA-WCHH-9X6H-7F6P documents the critical deprecation of the cryptographic library libolm (Olm) and its Python binding wrapper python-olm, which matrix-commander depended upon via its downstream client library matrix-nio. Multiple cryptographic vulnerabilities (timing leaks, side-channels, signature malleability, and protocol confusion) were disclosed in 2022 and 2024. Because libolm is unmaintained, Python clients using matrix-commander are considered cryptographically unsafe until migrating to vodozemac.