September 9, 2025

Lessons from the NPM Supply Chain Attack

Lessons from the Recent Supply Chain Attack

If you’ve been anywhere near the developer community in the last few hours, you’ve probably seen or heard the news.

A major supply chain attack struck the JavaScript ecosystem. Popular packages like chalk, strip-ansi, and color-convert (libraries with billions of weekly downloads) were compromised after the NPM account of a core maintainer was hijacked.

For many of us building in web3, SaaS, and beyond, this hits close to home. These are foundational dependencies buried deep in nearly every modern JavaScript project.

And the payload was a crypto-clipper, designed to silently hijack wallet transactions and swap out addresses.

The Real Risk Is Frontend, Not Contracts

Here’s the nuance that everyone needs to understand.

Smart contracts weren’t compromised. Browser wallets like MetaMask and Phantom weren’t compromised either. The attack vector was frontend code. 

If a dApp blindly updated its dependencies, malicious JavaScript could be injected into the production build. That code could then intercept transactions in the browser, rewrite wallet addresses, and trick users into signing away their funds.

This distinction matters. MetaMask and Phantom remained safe precisely because they practice strict version pinning and dependency auditing. They don’t just pull in the latest patch. 

Many dApp teams, unfortunately, don’t exercise the same discipline.

How Did This Slip Through?

What fascinates me most is how the attack was discovered.

Not through a security audit, not through threat intelligence feeds, but through… a simple build failure.

That “bug” turned out to be the attacker’s malware trying to call fetch in a Node.js environment that didn’t support it. If the environment had been slightly more modern, the attack might have gone undetected for much longer.

It’s a sobering reminder that sometimes the smallest quirks save us from bigger disasters.

How the Malware Works?

  1. Injects itself into the browser
    • Hooks core functions like fetch, XMLHttpRequest, and wallet APIs (window.ethereum, Solana, etc.).
    • Ensures it can intercept both web traffic and wallet activity.
  2. Watches for sensitive data
    • Scans network responses and transaction payloads for anything that looks like a wallet address or transfer.
    • Recognizes multiple formats across Ethereum, Bitcoin, Solana, Tron, Litecoin, and Bitcoin Cash.
  3. Rewrites the targets
    • Replaces the legitimate destination with an attacker-controlled address.
    • Uses “lookalike” addresses (via string-matching) to make swaps less obvious.
  4. Hijacks transactions before they’re signed
    • Alters Ethereum and Solana transaction parameters (e.g., recipients, approvals, allowances).
    • Even if the UI looks correct, the signed transaction routes funds to the attacker.
  5. Stays stealthy
    • If a crypto wallet is detected, it avoids obvious swaps in the UI to reduce suspicion.
    • Keeps silent hooks running in the background to capture and alter real transactions

Lessons for Founders, Builders, and Developers

This attack weaponizes both the complexity of modern build systems and the limits of human attention.

There are two attack vectors that should concern every developer - passive address swapping and active transaction hijacking. 

This incident underscores a hard truth: our open-source ecosystem runs on trust, but trust isn’t enough.

Here’s what we can do today:

1. Take Immediate & Automated Action

  • Audit and pin safe versions of compromised libraries (chalk, strip-ansi, color-convert, etc.) using overrides in package.json.
  • Automate vulnerability scanning with tools like Snyk, Dependabot, or Mend. Integrate them into CI/CD. These tools will automatically block builds or flag PRs that introduce known vulnerable or malicious dependencies.

2. Enforce Strict Versioning & Provenance

  • Practice strict versioning. Never blindly accept the latest tag. Use package-lock.json or npm-shrinkwrap.json to lock your entire dependency tree and update deliberately after validation.
  • Verify package provenance with signed packages (e.g., Sigstore) to ensure code truly comes from its source, not a hijacked account.

3. Harden Your Delivery Pipeline

  • Harden CI/CD pipelines. Treat a failed build as a security signal. Configure your pipelines to fail if security scans uncover critical issues.
  • Remove unused dependencies. Consider rewriting simple functionality in-house to avoid pulling in a massive tree of external code. This will minimize your attack surface.

4. Protect Users at Runtime

  • Secure the frontend. Use Subresource Integrity (SRI) and strict CSP to block tampered scripts. This ensures the browser will reject any script that has been tampered with, even if your CDN is compromised. Use strict Content Security Policies (CSP) to further lockdown what can execute.
  • Maintain an SBOM. A dependency inventory lets you instantly assess exposure when new threats appear. This gives you a nested inventory of every dependency, allowing you to instantly assess your exposure when a new vulnerability is announced.

As entrepreneurs, we thrive on speed. We ship fast, we rely on open-source, and we trust in community-driven software. But this incident is a brutal reminder that our very velocity can be weaponized against us.

The difference between a safe ecosystem and a compromised one often comes down to simple discipline:audit your code and respect the responsibility of pushing software that interacts with people’s money.

Stay safe!

My blog couldn't proceed your request right now.

Please try again a bit later.

Thank you for contacting me!

I will get back to you as soon as I can.

Contact me

Processing...

My blog couldn't proceed your request right now.

Please try again a bit later.

Thank you for subscribing!

I added you to my emailing list. Will let you know as soon as I have something interesting.

Subscribe for email updates

Processing...