Kai Ole Hartwig
8 min read
High

jscrambler npm package compromised: cross-platform Rust infostealer in versions 8.14.0–8.20.0

On 11 July 2026 at 16:12 BST, a compromised version of the JavaScript protection tool jscrambler was published to npm: 8.14.0 shipped a cross-platform, Rust-written infostealer packaged inside an obfuscated binary container. Jscrambler's own advisory confirms a compromised publishing credential as the root cause. Four more malicious versions followed within hours (8.16.0, 8.17.0, 8.18.0, 8.20.0) — from 8.18.0 onward with no install hook at all, evading scanners that only check scripts. Four dependent packages are also affected. The safe version is 8.22.0.

TL;DR — 90 seconds

Affected?

jscrambler 8.14.0/8.16.0/8.17.0/8.18.0/8.20.0, plus jscrambler-webpack-plugin 8.6.2, gulp-jscrambler 8.6.2, grunt-jscrambler 8.5.2, jscrambler-metro-plugin 9.0.2. Safe versions: 8.22.0 and 8.6.3/8.6.3/8.5.3/9.0.3 respectively.

Risk?

Cross-platform Rust infostealer: cloud keys (AWS Secrets Manager, GCP metadata, Azure IMDS), crypto wallets (MetaMask, Phantom, Exodus), AI tool configs (Claude Desktop, Cursor MCP servers), browser/Discord/Slack sessions, Kubernetes API access. Persistence via Windows scheduled tasks/macOS LaunchAgents; on Linux it loads eBPF programs directly into the kernel.

Immediate action?

Remove the affected version(s) immediately, update to 8.22.0 (or the respective safe version), rotate all credentials reachable from CI/dev environments that installed it.

Recommendation?

Even with only ~1,479 reported downloads across all malicious versions: anyone using jscrambler in CI/CD should check install logs for dist/setup.js execution, regardless of the exact version installed.

Criticality?

high (hero badge) — kernel-level access on Linux via eBPF, broad credential coverage.

What happened?

The incident began with a compromised npm publishing credential that let an attacker publish version 8.14.0 of jscrambler — with no matching commits in the public repository. Socket detected the compromised version roughly six minutes after publication. Jscrambler itself caught the incident “within seconds” via automated maintainer notifications and immediately deprecated the version.

The attacker kept going: the same day brought 8.16.0 and 8.17.0 (still delivered via a preinstall hook), then 8.18.0 and 8.20.0 — where the install hook disappears entirely and the same dropper is instead injected as a self-executing function at module import time, defeating scanners that only check install scripts. The payload consists of platform-specific binaries for Linux, macOS and Windows, embedded in an obfuscated container (dist/intro.js, reportedly around 7.8 MB), loaded via a small loader (dist/setup.js).

Beyond the main package, four dependent packages are affected: jscrambler-webpack-plugin 8.6.2, gulp-jscrambler 8.6.2, grunt-jscrambler 8.5.2 and jscrambler-metro-plugin 9.0.2.

Who is affected?

PackageAffected version(s)Safe version
jscrambler8.14.0, 8.16.0, 8.17.0, 8.18.0, 8.20.08.22.0
jscrambler-webpack-plugin8.6.28.6.3
gulp-jscrambler8.6.28.6.3
grunt-jscrambler8.5.28.5.3
jscrambler-metro-plugin9.0.29.0.3

Per the Jscrambler advisory, total downloads across all malicious versions are around 1,479 — the actual compromise rate (how many installs actually executed the payload) is not quantified by that number. Primary targets are development environments and CI/CD systems with access to deployment tokens and source code.

Impact

The infostealer casts a wide net: cloud credentials (AWS, Azure, GCP), crypto wallets (MetaMask, Phantom, Exodus), password managers, browser sessions, and — notably — configurations for AI tools like Claude Desktop and Cursor MCP servers. Particularly relevant for CI/CD environments: Kubernetes API access is captured too. Persistence is platform-dependent via Windows scheduled tasks or macOS LaunchAgents, including anti-debugging checks. On Linux, the malware loads eBPF programs directly into the kernel — a materially deeper access level than typical userspace stealers. Per Socket's analysis, exfiltration runs over TLS (via the rustls library) using multipart form uploads; specific C2 domains/IPs are not disclosed in the public analyses.

For teams that use jscrambler to protect their own production bundles, the irony is hard to miss: a security tool itself became the entry point.

Mitigation / immediate steps

Note: based on the official Jscrambler advisory and Socket's analysis — reconcile concrete version levels against the primary sources.

Operational decision block

Step 1 — remove the affected version and update

 

# check lockfile / package.json for affected versions
grep -E "jscrambler|gulp-jscrambler|grunt-jscrambler" package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null
# hard-pin to the safe version
npm install jscrambler@8.22.0 --save-exact
npm install jscrambler-webpack-plugin@8.6.3 gulp-jscrambler@8.6.3 grunt-jscrambler@8.5.3 jscrambler-metro-plugin@9.0.3 --save-exact 2>/dev/null

 

Step 2 — check install logs and CI history

 

# search CI build logs for dist/setup.js execution
grep -RIn "dist/setup.js\|dist/intro.js" ci-logs/ 2>/dev/null
# check the node_modules cache for affected versions
find . -path "*/node_modules/jscrambler/package.json" -exec grep -H '"version"' {} \;

 

Step 3 — rotate credentials

 

- AWS/Azure/GCP keys on any CI runner that installed the package
- npm/registry publishing tokens
- deployment and repository tokens
- browser/Discord/Slack sessions on affected developer machines

 

Step 4 — restrict install scripts as a baseline

 

# CI: don't auto-run install scripts
npm ci --ignore-scripts
# then explicitly approve what's actually needed
npm approve-scripts

Detection / verification

Version & lockfile check

 

npm ls jscrambler jscrambler-webpack-plugin gulp-jscrambler grunt-jscrambler jscrambler-metro-plugin 2>/dev/null

 

Check for execution traces

 

# was setup.js/intro.js ever executed? check CI logs and shell history
grep -RIn "dist/setup.js\|dist/intro.js" . 2>/dev/null
# unusual scheduled tasks (Windows) or LaunchAgents (macOS) on build machines
schtasks /query /fo LIST /v 2>nul | findstr /i "jscrambler"
ls ~/Library/LaunchAgents/ 2>/dev/null | grep -i jscrambler

 

Runtime indicators

 

- unexpected outbound TLS traffic from build/CI runners shortly after npm install
- new eBPF programs on Linux build hosts (bpftool prog list)
- unfamiliar AWS/GCP/Azure API calls shortly after CI runs involving the affected package

Operator guidance

Mid-market

Check lockfiles for the affected versions, update to 8.22.0 (or the respective safe version), and proactively rotate credentials from any CI environment that ever installed the package — even without direct evidence of compromise.

Enterprise

Run an SBOM/dependency scan across all repositories for the five affected packages, including transitive dependencies. Particularly relevant: from version 8.18.0 onward the attack works without an install hook — scanners that only check preinstall/postinstall scripts miss these variants. Add static analysis at the module-import level.

CI/CD

Default to --ignore-scripts in CI pipelines, use an explicit allowlist via npm approve-scripts for install scripts that are genuinely needed, and review outbound network traffic from build runners (egress filtering would at least have complicated the TLS exfiltration).

Frequently asked questions about the jscrambler incident

What if I have --ignore-scripts set globally in CI?+

That protects against the early variants (8.14.0-8.17.0, install hook), but not against 8.18.0/8.20.0, where the dropper runs on module import. Version pinning remains the more reliable control either way.

Does this only affect npm users, or other distribution channels too?+

The advisory concerns the npm registry publication; per Jscrambler, the root cause — a compromised publishing credential — has since been revoked and rotated. Other distribution channels are not named as affected in the public sources.

Why did versions from 8.18.0 onward ship without an install hook?+

To evade scanners that only check preinstall/postinstall scripts. The dropper was instead moved into the module code itself as a self-executing function, triggered on import.

Do I really need to rotate my AWS/GCP keys without evidence of compromise?+

If an affected version was installed in a CI environment with access to those keys: yes. The malware exfiltrates automatically, and the absence of explicit evidence is not proof of safety.

Is it enough to just remove 8.14.0?+

No — 8.16.0, 8.17.0, 8.18.0 and 8.20.0 are compromised too, and from 8.18.0 onward the install hook is missing, which complicates detection. Hard-pin to 8.22.0 or the safe version of whichever add-on packages you use.

Conclusion

The jscrambler incident demonstrates two things at once: how fast attackers iterate after a compromised publishing credential (five malicious versions in one day), and how deliberately newer npm supply-chain malware now evades scanners that only look at install scripts. That a security tool itself was the vector underscores the point: trusting a package category is no substitute for version pinning, lockfile audits, and egress control in CI.

Sources

I audit your lockfiles for compromised packages, restrict install scripts in CI, and rotate affected credentials.

Dependency audits against current supply-chain incidents, rollout of --ignore-scripts/approve-scripts allowlisting in CI, egress filtering for build runners, and credential rotation after suspected compromise.

Platform operations instead of advice on paper: I continuously check, harden and validate your npm/CI supply chain.

About the author

[Translate to English:] Foto von Kai Ole Hartwig.

Kai Ole Hartwig

Freelance DevSecOps consultant · OnlyOle Consulting

Programming since 2002 – self-taught, set up my own business with KO-Web in 2012. Over 100 projects, with a focus on security, performance, automation and quality. Today freelance: DevSecOps consulting, training and software development.

IronWorm, npm, supply chain, worm, Rust, eBPF, rootkit, Trusted Publishing, OIDC, GitHub Actions, credential harvesting, CI/CD, Shai-Hulud, JFrog, Kubernetes, Docker, Bun, DevSecOps

IronWorm (Rust npm worm)

The Rust npm worm IronWorm runs via a preinstall hook, harvests 86 environment variables including AI provider keys and ~/.claude credentials, self-publishes in CI via npm Trusted Publishing, exfiltrates without C2 through swapped GitHub Actions workflows, and hides behind an eBPF rootkit (which fails under kernel lockdown). With mitigation, detection IOCs, a root-cause deep dive and operator guidance; package list delegated to JFrog.

npm, supply chain, Mini Shai-Hulud, Miasma, TeamPCP, @redhat-cloud-services, preinstall, GitHub Actions, OIDC, CI/CD, credential theft, cloud identity, AWS, GCP, Azure, Kubernetes, MCP, dead man switch, DevSecOps, NIS-2, GDPR, Mittelstand

Miasma npm worm (@redhat-cloud-services)

Incident analysis of the Miasma wave: 32 compromised @redhat-cloud-services npm packages, a variant of TeamPCP's open-sourced Mini Shai-Hulud. Patient zero was a compromised Red Hat employee GitHub account with orphan commits to two RedHatInsights repos; publishing ran via GitHub Actions OIDC trusted publishing including Sigstore. A preinstall hook runs a 4.2 MB obfuscated loader, harvests GitHub/AWS/GCP/Azure/Kubernetes/Vault/npm/SSH/Docker credentials, newly also collects GCP and Azure cloud identities, installs kitty-monitor persistence and a destructive gh-token-monitor dead-man switch. Operational assessment: critical. Order: isolate, remove persistence, then rotate.

npm, binding.gyp, node-gyp, supply chain, worm, credential harvesting, CI/CD, GitHub Actions, Bun, AES-128-GCM, dangling commit, autotel, awaitly, node-env-resolver, StepSecurity, ignore-scripts, DevSecOps

binding.gyp npm worm (node-gyp)

Ongoing npm supply chain incident: a self-replicating worm uses binding.gyp/node-gyp instead of postinstall, downloads the Bun runtime, harvests cloud/registry credentials, injects setup-bun into GitHub Actions workflows and poisons further packages of the victim. With mitigation, detection IOCs and operator guidance — package list delegated to the primary source.