Skip to main content
December 18, 2025 6 min read Jeremiah Coakley

How Araptus' PNPM Security Scanner Caught Malware in My Dependencies

Self-hosting Araptus' PNPM Supply Chain Security Scanner led to discovering protest-ware hiding in transitive dependencies—before it reached production.

ARAPTUS Supply Chain Security Scanner - Scan your npm dependencies for malicious packages

It Started With a Message

A few days ago, Kris from Araptus shared something with me: he'd just released an open-source npm security scanner and blogged about it. The tool was inspired by the Shai-Hulud worm—a self-replicating malware that infected 187+ npm packages with over 2 million weekly downloads back in September.

"Hey, if you have any ideas on how to evolve this, let me know," he said.

I had a few ideas.

Adding Deep Scanning and a Web UI

Kris's original scanner checked direct dependencies in package.json. That's useful, but most supply chain attacks hide in transitive dependencies—the packages your packages depend on. You never installed them directly, so you might not even know they exist.

I contributed the deep scanning capability—analyzing lock files to scan transitive dependencies. This was the key feature that would later catch the malware. I pushed the changes to Kris for review, then built the self-hosted web UI while the pull request was still open.

Deep Scanning (--deep flag)

Analyzes lock files (pnpm-lock.yaml, package-lock.json, yarn.lock) to scan the entire dependency tree—showing dependency chains so you can trace exactly how malicious packages entered your project. This was the feature that caught the malware.

I built the self-hosted web UI—a drag-and-drop interface with animated scan progress, real-time package counters, expandable issue cards, and one-click copy for remediation commands. Built with Astro 5 + React 19, the UI is designed to be self-hosted on your own infrastructure. I implemented it to my home server using my fork with the deep scanning feature, which is what led to discovering the malware.

After I discovered the malware and documented additional findings, Kris merged both contributions into the repository. The deep scanning capability was what caught the malware, and the self-hosted UI made it easy to run scans across all my projects.

Self-Hosting the UI

The web UI is designed to be self-hosted—giving you full control over your security scanning infrastructure. After implementing the UI to my home server infrastructure, I decided to run the scanner across all my Node.js projects. I've been consolidating my development infrastructure lately—centralizing repositories, improving security posture. This seemed like the perfect opportunity to test the scanner in a real-world environment.

I set up automated scanning to run daily across all my Node.js projects. The first scan covered about 15 repositories.

14 came back clean. One didn't.

The Finding

$ ./security-audit.sh --deep
Repos scanned: 15
Clean repos: 14
Repos with issues: 1
Issues by severity:
Critical: 1

The scanner flagged es5-ext in a web application I was working on. This was code that was about to go into production.

What is es5-ext?

es5-ext is a utility package that, in March 2022, had code added to check if your system locale is Russian or Belarusian—and if so, displays a political message about the Ukraine war. While not destructive like some malware, it executes arbitrary code based on locale detection without user consent. It's classified as protest-ware.

The deep scanning feature I'd just added is what caught it. The package wasn't a direct dependency—it was buried three levels deep:

# Dependency chain
web-app
└── memoizee (direct dependency)
└── es5-ext ← MALICIOUS

And here's the kicker: memoizee was never even used in the codebase. Someone had installed it at some point, tried it, abandoned it, and never removed it. An unused dependency brought protest-ware into the project.

Five Minutes to Remediate

Once I knew the issue, the fix was straightforward:

# Verify memoizee wasn't actually used
$ grep -r "memoizee" src/
# No results
# Remove it
$ pnpm remove memoizee @types/memoizee
# Verify
$ ./security-audit.sh --deep
# Clean repos: 15, Issues: 0

The lock file diff: -294 lines of transitive dependencies removed. The code never made it to production.

The Takeaway

This wasn't a sophisticated attack. It wasn't a zero-day. It was a known protest-ware package that had been sitting in the dependency tree for probably years, just waiting to potentially cause confusion or compliance issues.

I only found it because:

  • 1
    Kris built and shared a tool with me
  • 2
    I added deep scanning to catch transitive dependencies
  • 3
    I decided to run it across everything when setting up new infrastructure

Without that chain of events, this would have shipped to production. I would never have known—until maybe a security audit flagged it, or a Russian-locale user reported unexpected behavior.

What Else the Scanner Has Caught

The scanner has also caught other notable real-world detections that show why deep transitive scanning matters:

🔥

node-ipc: The Destructive Protest-Ware

CVE-2022-23812 CVSS 9.8

Unlike es5-ext's console messages, node-ipc v10.1.1 was destructive. It detected Russian/Belarusian IP addresses and overwrote files with heart emojis. This affected Vue CLI, Unity Hub, and over a million weekly downloads.

# Dependency chain detected
project → @vue/cli → @vue/cli-shared-utils → node-ipcpeacenotwar

Remediation: Updating @vue/cli to the latest version removes the entire malicious chain.

⚠️

rc: The Abandoned Author Threat

5 LEVELS DEEP NO CVE

The rc package was never directly sabotaged, but its author (Marak) demonstrated willingness to destroy his own packages when he sabotaged colors and faker. Since then, rc has been abandoned and flagged—but it has no CVE, so npm audit doesn't catch it.

# 5 levels deep in the dependency tree
project → vercel → update-notifier → latest-version → package-json → registry-auth-token → rc

Remediation: Updating the parent vercel package to v50+ removes the vulnerable chain entirely.

Both of these would have been missed by standard npm audit—one because it lacks a CVE, and one because it requires deep transitive scanning to detect. This is why we built the scanner.

Scan Your Dependencies

The tool is free and open source. The web UI is designed to be self-hosted on your own infrastructure for full control and privacy. Clone the repository and self-host it yourself.

Credits

Kris from Araptus built the original scanner, maintains the threat database, and contributed the multi-project scanner. His blog post covers the 2025 npm supply chain crisis in detail—including the Shai-Hulud worm, cryptocurrency hijacking attacks, and the 150,000-package token farming scam.

I contributed the deep scanning engine, web UI visualization enhancements—animated scan progress, campaign timeline, copy-to-clipboard commands—and the server integration. After I discovered the malware and documented additional findings, Kris merged the pull request. It's been a good collaboration.

Subscribe to Security Insights

Get enterprise security tips, compliance guides, and best practices delivered to your inbox.