Skip to content
Security First — Part 18 of 30

Dependency Security: Supply Chain Attacks via npm

Written by claude-sonnet-4 · Edited by claude-sonnet-4
supply chain securitynpm securitytyposquattingpostinstall scriptsdependency confusionmalicious packagesmaintainer compromiseaxios attackslopsquattingvibe codingJavaScript securitypackage manager securitynpm auditopen source security

Security First — Part 18 of 30


It's 12:30 AM on April 1, 2026. A developer in a CI/CD pipeline halfway through a routine deploy types npm install. Thirty seconds later, a remote access trojan is quietly running on their machine, phoning home to an attacker-controlled server. Credentials are harvested. SSH keys are exfiltrated. The malware then deletes itself — leaving no obvious trace in the installed packages.

The developer had run npm audit earlier that week. It came back clean.

This wasn't a hypothetical exercise. On March 31, 2026, attackers compromised the npm account of the lead maintainer of axios — one of the most downloaded JavaScript libraries on the planet, with over 100 million weekly downloads. Within 39 minutes of each other, two malicious versions were published: axios@1.14.1 and axios@0.30.4. They were live on the registry for about three hours before npm pulled them.

Three hours. That's all it takes.

Yesterday's article covered dependency scanning tools — npm audit, pip-audit, Snyk, and friends. Those tools are important. But they have a hard limit: they catch known vulnerabilities in known packages. They do almost nothing against supply chain attacks, where the weapon is the package itself, freshly poisoned.

Today we go deeper. We're going to look at how these attacks actually work — the mechanics, the psychology, the real incidents — and what you, a vibe coder building with AI tools, can do to protect yourself beyond just running audit commands.


Three Ways Attackers Get Into Your node_modules

1. Typosquatting: The Fat-Finger Trap

Imagine you're asking an AI coding assistant to add an HTTP client to your project. It suggests:

npm install axois

Notice the transposed o and i? That's a real package on npm. So are chalk-cli (vs chalk), lodash-utils (vs lodash), and hundreds more. Attackers register these misspellings hoping you — or your AI assistant — will make the mistake.

In June 2025, researchers at Checkmarx uncovered a campaign targeting packages like colorama (one of Python's most-used terminal color libraries). The attackers published coloramapkgs and colorizator to PyPI, mimicking both Python and JavaScript naming conventions to cast a wider net. The malicious packages delivered platform-specific payloads: on Windows, they disabled Windows Defender and harvested environment variables; on Linux, they opened encrypted persistent backdoors using tools like gs-netcat.

Here's the extra-cruel twist for vibe coders: your AI assistant can be fooled too. A 2025 study from UT San Antonio, Virginia Tech, and the University of Oklahoma tested 16 code-generation models — including GPT-4, Claude, and CodeLlama — generating 576,000 code samples. The models regularly hallucinated package names that don't exist. Attackers now register those hallucinated names pre-emptively — a technique researchers call "slopsquatting." If your AI suggests a package and you install it without verification, you may be installing something an attacker parked there months ago, waiting for exactly this moment.

2. Maintainer Compromise: Hijacking the Keys

This is the scariest category, because the package you trust is the package that attacks you.

The axios incident is the clearest recent example. The attacker didn't touch a single line of axios's actual source code. Instead, they:

  1. Stole the npm access token of jasonsaayman, axios's primary maintainer
  2. Changed the account's registered email to an attacker-controlled ProtonMail address, locking out the real owner
  3. Published two new versions of axios that looked legitimate — same source, same everything — but with one extra line in package.json:
"dependencies": {
  "follow-redirects": "^1.15.11",
  "form-data": "^4.0.5",
  "proxy-from-env": "^2.1.0",
  "plain-crypto-js": "^4.2.1"
}

That last line — plain-crypto-js — was the weapon. It was a typosquat of the legitimate crypto-js library, published from a throwaway account 18 hours before the axios attack to build up a brief history on the registry (evading tools that flag brand-new packages).

plain-crypto-js was never imported anywhere in axios's code. It was a phantom dependency — its only purpose was to run its postinstall script when you ran npm install.

Similar mechanics were at the heart of the September 2025 npm phishing attack, where attackers registered the domain npmjs.help, then sent emails to maintainers impersonating npm support: "Your 2FA credentials need to be updated by September 10." Maintainer Josh Junon later wrote: "Email came from support at npmjs dot help. Looked legitimate at first glance. Not making excuses, just had a long week and a panicky morning." That one phished account led to 200+ compromised packages affecting billions of weekly downloads, including chalk, debug, strip-ansi, and ansi-regex — packages that are transitive dependencies of nearly every JavaScript project in existence.

3. Malicious postinstall Scripts: Code That Runs Before Your Code

This is the technical mechanism that makes all of the above so devastating — and so poorly understood by most developers.

When you run npm install, npm doesn't just download files. It executes code. Any package in your dependency tree can define lifecycle scripts in its package.json:

{
  "scripts": {
    "preinstall": "node setup.js",
    "postinstall": "node setup.js"
  }
}

These scripts run automatically, with full access to your filesystem, environment variables, and network — before you've touched a single line of your own code. There's no sandbox. No confirmation dialog. No warning.

In the axios attack, plain-crypto-js@4.2.1's postinstall hook ran node setup.js — a heavily obfuscated dropper that:

  • Detected your operating system
  • Reached out to the attacker's server at sfrclak.com:8000
  • Downloaded and executed a platform-specific remote access trojan (RAT): a C++ binary on macOS, a PowerShell script on Windows, a Python script on Linux
  • Then deleted itself — removing setup.js, replacing the malicious package.json with a clean decoy stub renamed from package.md

After the attack, if you inspected node_modules/plain-crypto-js, you'd see a perfectly clean package with no evidence of a postinstall script ever having existed. npm audit would find nothing. Manual review would find nothing.

This is why the Trail of Bits security team wrote in September 2025: "Dependency scanning only catches known vulnerabilities. It won't catch when a compromised maintainer publishes malware."


What the Attack Actually Looks Like From Your Terminal

Here's the terrifying thing: the axios attack output looked completely normal:

$ npm install axios

added 5 packages, and audited 6 packages in 1s

found 0 vulnerabilities

Zero vulnerabilities. Clean bill of health. Meanwhile, 1.1 seconds into that install, your machine was already calling home to an attacker's server.

This is what distinguishes supply chain attacks from traditional vulnerabilities. There's no CVE. No security advisory. No audit tool alert. The attack is happening during installation, not during execution of vulnerable code.


Five Things You Can Do Right Now

Here's the practical toolkit. None of these require deep security expertise — they're configuration and habit changes that dramatically reduce your attack surface.

1. Use npm ci Instead of npm install in Any Automated Context

# Use this in CI/CD, Docker builds, and scripts:
npm ci

# NOT this:
npm install

npm ci installs exactly what's in your package-lock.json and fails if there's any discrepancy. npm install resolves dependencies fresh every time, which means a new malicious version of a package can silently replace a safe one.

Commit your lockfile. If package-lock.json is in your .gitignore, fix that right now.

2. Disable Lifecycle Scripts in Automated Builds

# Blocks all preinstall/postinstall/install scripts:
npm ci --ignore-scripts

# You can also set this globally:
npm config set ignore-scripts true

This is the single most effective mitigation against the postinstall attack vector. Most packages don't need lifecycle scripts to function — they're used for things like compiling native modules. If a build breaks with --ignore-scripts, investigate why before removing the flag.

3. Set a Minimum Package Release Age

npm config set min-release-age 72h

This setting (introduced in npm 10+) blocks packages published less than 72 hours ago from being installed. The axios attack would have been completely blocked by this setting — the malicious plain-crypto-js@4.2.1 was only a few hours old when the attack ran. The attacker's 18-hour staging gap still wouldn't have been enough.

4. Always Verify Package Names Before Installing

Never trust an AI assistant's package suggestion blindly. Before running npm install <package>, spend 30 seconds checking:

# Check the npm registry page first:
npm info <package-name>

# Look for:
# - Download counts (legitimate popular packages have millions)
# - Publish date (very new = higher risk)
# - Repository link (should match a real GitHub project)
# - Maintainer count and history

For the colorama typosquatting campaign, a quick npm info coloramapkgs would have shown a package with zero weekly downloads and a publish date from that week. That's a red flag.

5. Check for Provenance on Critical Packages

Npm now supports Sigstore-based provenance attestations — cryptographic proof that a package was built from a specific GitHub Actions workflow on a specific commit. The axios attack was detectable because the legitimate axios versions are published via GitHub Actions with OIDC Trusted Publisher, but the malicious versions were published manually with a stolen token.

# Check if a package has provenance:
npm info axios dist.attestations

# A legitimate recent axios publish shows:
# predicateType: 'https://slsa.dev/provenance/v1'
# bundleFormat: 'application/vnd.dev.sigstore.bundle.v0.3+json'

If a new version of a package that normally has provenance shows up without it, treat that as a compromise indicator.


Quick Detection: Was I Hit?

If you think you might have installed a compromised package, here's how to check for the most recent incidents:

# Check for compromised axios versions (March 2026 attack):
npm list axios 2>/dev/null | grep -E "1\.14\.1|0\.30\.4"

# Check for the phantom dependency:
ls node_modules/plain-crypto-js 2>/dev/null && echo "POTENTIALLY COMPROMISED"

# Check for persistence artifacts on Windows (PowerShell):
Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' | Select-Object MicrosoftUpdate

# Check for macOS artifacts:
ls /Library/Caches/com.apple.act.mond 2>/dev/null && echo "CHECK THIS FILE"

If you find signs of compromise: isolate the machine from the network immediately, do not attempt to clean in place, rebuild from a known-good image, and rotate every credential on that machine — npm tokens, SSH keys, AWS/GCP/Azure credentials, .env files, everything.


The Uncomfortable Truth About Vibe Coding

You're using AI to build software faster than ever. That's genuinely great. But AI assistants have a specific weakness in this threat landscape: they will confidently suggest package names, including ones that don't exist — and attackers are now pre-registering those hallucinated names at scale.

In early 2025, Google's AI Overview surfaced a recommendation for @async-mutex/mutex — a malicious typosquat of the legitimate async-mutex library (4 million+ weekly downloads) — as a credible search result. Developers were following that recommendation.

AI makes you fast. But fast + unverified is exactly what supply chain attackers are counting on.

The habit you need: before npm install <anything>, spend 30 seconds on the npm registry page. It's the fastest security check you can do, and it catches the majority of these attacks cold.


Action Checklist

  • Add package-lock.json to version control if it isn't already. Remove it from .gitignore.
  • Replace npm install with npm ci in all CI/CD pipelines, Dockerfiles, and deployment scripts.
  • Add --ignore-scripts to CI installs: npm ci --ignore-scripts
  • Set minimum release age: npm config set min-release-age 72h
  • Before every npm install <package>, check the registry page: download counts, publish date, maintainer history, linked repository.
  • Never install a package suggested by an AI without verifying it exists at npmjs.com first.
  • Enable 2FA on your npm account — phishing works, but TOTP/hardware keys stop credential replay.
  • Check provenance for critical dependencies: npm info <package> dist.attestations
  • Set up registry alerts for packages you depend on (Socket.dev and Phylum both offer free monitoring).
  • If you think you were hit: isolate, rebuild from clean image, rotate all credentials.

Ask The Guild

Community prompt: Have you ever accidentally installed a typosquatted package, or caught a suspicious dependency in a project? What was your first indicator something was wrong — and what did you do about it? Share your story in the comments. Bonus: what's the weirdest or most suspicious package name you've encountered in the wild?


Tom Hundley is a software architect with 25 years of experience. He has watched the npm registry grow from a curiosity to the single largest software repository in human history — and the single largest attack surface. He writes for developers who build fast and want to stay secure while doing it.

Copy A Prompt Next

Start safely

If this article changed how you think about the problem, copy a prompt that turns that judgment into one safe, reviewable next step.

Matching public prompts

6

Keep the task scoped, copy the prompt, then inspect one reviewable diff before the agent continues.

Need the safest first move instead? Open the curated sample prompts before you browse the broader library.

Start Here — Build Safely With AIStart Here — Build Safely With AI

What You're Actually Doing When You Build With AI

A plain-English explanation of the job: AI writes fast, you still choose scope, inspect output, and own the result.

Preview
"I am completely new to vibe coding and I want to build one very small thing safely.
The problem is: [describe the problem]
The user is: [describe the user]
The smallest useful version would do only: [describe the tiny outcome]
Before writing any code:
Security First

Turn this security lesson into a repeatable review habit

This article gives you the judgment call. The security paths give you the vocabulary, checklists, and repetition to catch the next issue before it reaches users.

Best Next Path

Compliance and Professional Security

Guild Member · $29/mo

Translate security advice into PCI, HIPAA, SOC 2, and real audit-facing controls instead of hand-wavy best practices.

15 lessonsIncluded with the full Guild Member library

Need the free route first?

Start with Start Here — Build Safely With AI if you want the workflow and vocabulary before you dive into the deeper path above.

T

About Tom Hundley

Tom Hundley writes for builders who need stronger technical judgment around AI-assisted software work. The Guild turns production experience into public articles, copy-paste prompts, and structured learning paths that help non-software developers supervise AI agents more safely.

Do this next

Leave this article with one concrete move. Copy the matching prompt, or start with the path that teaches the safest next skill in sequence.