How to Protect Your Business from npm Supply Chain Attacks

npm supply chain attacks are not just a developer problem. They are a credential, CI/CD, and business risk problem.

Does your website use Node.js or javascript?

There is a good chance that under the hood, you are using npm packages to make everything work.

You may be exposed to the recent glut of npm supply chain attacks.

The good news: you do not need to panic. Start with the basics:

  1. Use lockfiles.
  2. Review dependency changes.
  3. Keep secrets out of build jobs.
  4. Enable MFA.
  5. Scan for exposed credentials.
  6. Have a plan to rotate keys fast.

npm risk is not just a developer problem. It is a business risk.

How to Protect Your Business from npm Supply Chain Attacks

npm supply chain issues have been getting a lot of attention lately, and for good reason.

A lot of modern applications depend on hundreds or even thousands of open source packages. That is not automatically bad. Open source software powers a huge portion of the internet. But it does mean businesses need to understand one important thing:

A dependency is code you are choosing to trust.

When an npm package is compromised, the risk is not limited to the application itself. A malicious package may run during installation, look for secrets, steal GitHub tokens, access cloud credentials, modify build scripts, or tamper with your deployment pipeline.

That makes npm supply chain risk more than a software development issue.

It is a credential risk.

It is a CI/CD risk.

It is a business risk.

The good news is that there are practical steps you can take to reduce the risk.

Start With the Right Mindset

You probably cannot prevent every bad package from entering the npm ecosystem. That is not a realistic goal for most businesses.

The better goal is this:

Make sure a bad package cannot do much damage if it does get pulled into your environment.

That means limiting access, protecting secrets, reviewing dependency changes, and making your build process more predictable.

Use Lockfiles and npm ci

One of the simplest things you can do is make sure your builds are repeatable.

Use npm ci in your CI/CD pipeline instead of npm install.

npm ci

The difference matters.

npm ci installs dependencies based on your package-lock.json file. That helps prevent unexpected dependency changes from sneaking into your build.

You should also commit your lockfile to source control:

git add package-lock.json
git commit -m "Lock npm dependencies"

This does not eliminate supply chain risk, but it reduces surprise changes. When every build pulls whatever version happens to match a version range, you are accepting more risk than you may realize.

Avoid Automatically Installing Brand-New Package Releases

Many malicious packages are discovered fairly quickly after they are published.

That means a short waiting period can help.

Instead of automatically upgrading dependencies the moment a new version is released, consider using a cooling-off period. For many businesses, waiting 24 to 72 hours before adopting a new npm package version is reasonable. For higher-risk applications, waiting longer may make sense.

Tools like Dependabot or Renovate can help manage this process, but dependency updates should still be reviewed like any other code change.

A reasonable approach might look like this:

Patch updates: review before merging
Minor updates: review before merging
Major updates: planned and tested change
Brand-new dependencies: extra scrutiny required

Dependency updates are not just maintenance tasks. They are new code entering your environment.

Disable Install Scripts Where Practical

Many npm attacks abuse install scripts.

Packages can include scripts such as:

preinstall
install
postinstall

These scripts may run automatically when the package is installed. That can be useful for legitimate packages, but it is also attractive to attackers.

Where possible, consider running:

npm ci --ignore-scripts

This prevents install scripts from running automatically.

This may not work for every project. Some packages, especially native modules, may require install scripts to function correctly. But where you can disable scripts, it is a strong control.

At minimum, you should know which packages require install scripts and why.

Keep Secrets Away From Dependency Installation

This is one of the most important controls.

A malicious package running during installation should not have access to production secrets.

A risky CI/CD flow looks like this:

Install dependencies
Build application
Deploy to production

All in one job, with sensitive credentials available the entire time.

A safer flow looks like this:

Job 1: Install dependencies with no production secrets
Job 2: Build and test with minimal access
Job 3: Deploy with production credentials available only when needed

The install step should not have access to:

GitHub tokens
npm publishing tokens
AWS, Azure, or GCP credentials
SSH keys
Slack or Teams webhooks
Production database credentials
Deployment secrets

If a malicious package runs during npm ci, it should land in a low-privilege environment with nothing valuable to steal.

That is the real goal.

Use Short-Lived Credentials

Long-lived credentials create long-lived risk.

Where possible, avoid static cloud keys and long-lived tokens in CI/CD environments.

Instead, use short-lived credentials through OIDC or your cloud provider’s native identity features.

This is especially important for credentials like:

AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AZURE_CLIENT_SECRET
GCP_SERVICE_ACCOUNT_KEY
GitHub personal access tokens
npm tokens

If a token is stolen, you want the damage window to be short.

Short-lived credentials are not magic, but they dramatically reduce the blast radius of a compromise.

Turn On Secret Scanning

Assume attackers are looking for secrets.

At minimum, businesses using GitHub should look at:

GitHub secret scanning
GitHub push protection
CI/CD audit logging
Cloud provider alerts
npm token monitoring

Secret scanning helps identify credentials that have accidentally been committed to repositories or exposed in other ways.

Push protection can stop some secrets from being committed in the first place.

These controls are not exciting, but they are extremely practical.

Review Dependency Changes Like Code Changes

A dependency update is code entering your application.

Treat it that way.

When reviewing dependency changes, ask questions like:

Is this a new direct dependency?
Why do we need it?
How widely used is the package?
Was the package recently created?
Did the maintainer change?
Did the package add new install scripts?
Did it add a large dependency tree?
Does the package access the network, filesystem, or environment variables?
Is the package name similar to another popular package?

Be especially careful with dependencies that touch:

Authentication
Authorization
Build systems
Deployment tooling
Cloud SDKs
Logging
Security controls
Cryptography
CI/CD workflows

The more sensitive the role of the package, the more scrutiny it deserves.

Use npm audit, But Do Not Stop There

npm audit is useful, and you should use it.

npm audit

For production dependencies, you can also run:

npm audit --production

But it is important to understand what npm audit does and does not do.

It is better at identifying known vulnerable packages than identifying brand-new malicious packages.

That means npm audit is a good baseline, but it should not be your only control.

Supply chain attacks often happen before traditional vulnerability databases catch up.

Consider a Private Package Proxy or Cache

For businesses with multiple developers or production applications, a private npm proxy or cache can be a valuable control.

Examples include:

GitHub Packages
JFrog Artifactory
Sonatype Nexus
Cloudsmith
Verdaccio

The idea is to avoid having every developer machine and CI/CD runner pull directly from the public npm registry.

Instead, the flow becomes:

Developer or CI/CD system
Approved package cache
Public npm registry

This gives you a place to:

Cache known-good versions
Block known-bad versions
Review new packages
Prevent dependency confusion
Create an approval process
Improve visibility into package usage

For smaller teams, this may feel like overkill. For businesses that depend heavily on software delivery, it can be a very reasonable investment.

Watch for Dependency Confusion

Dependency confusion happens when a build system accidentally pulls a public package instead of an internal private package.

This can happen when internal package names are not properly scoped or registry configuration is loose.

To reduce this risk:

Use scoped internal packages
Configure your registry carefully
Avoid unscoped internal package names
Route private scopes only to your private registry

For example:

@yourcompany:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}

The goal is to make sure internal packages come from internal sources, not from the public internet.

Protect Your npm and GitHub Accounts

Supply chain attacks are not always about tricking someone into installing a bad package. Sometimes attackers compromise legitimate maintainers, repositories, or publishing workflows.

That means account security matters.

For developers and maintainers:

Use MFA or passkeys
Do not share npm accounts
Use least-privilege tokens
Remove old tokens
Protect release branches
Require pull request reviews
Review GitHub Actions workflows
Prefer trusted publishing where available

If your company publishes npm packages, this becomes even more important.

A compromised publishing account can turn your legitimate package into someone else’s attack path.

Have an Incident Playbook

When a compromised package is reported, you do not want to make up the response as you go.

Have a simple playbook.

Start with:

1. Search package-lock.json files for the affected package and version.
2. Check whether the package is direct or transitive.
3. Identify where it was installed.
4. Check developer machines, CI/CD runners, and build containers.
5. Remove or pin away from the affected version.
6. Rebuild from a clean environment.
7. Rotate exposed credentials.
8. Review GitHub, npm, cloud, and CI/CD audit logs.
9. Look for suspicious commits, workflows, tokens, releases, or deployments.

The key step is credential rotation.

Do not just remove the bad package and move on.

If a malicious package ran in an environment that had access to secrets, assume those secrets may have been exposed.

A Practical Policy for Small Businesses

You do not need a hundred-page policy to get started.

A simple npm dependency policy could say:

We use lockfiles and npm ci for repeatable builds.
Dependency updates are reviewed before merging.
New dependencies require extra review.
CI/CD install jobs do not receive production secrets.
Install scripts are disabled where practical.
Secrets are short-lived where possible.
GitHub and npm accounts require MFA.
Known compromised dependencies trigger credential rotation.

That is not flashy, but it is practical.

And practical is the point.

The Bottom Line

npm supply chain risk is not just a developer problem.

It is not just an open source problem.

It is a business risk tied directly to credentials, build systems, release pipelines, and production access.

The goal is not perfection. The goal is to make smart, practical improvements that reduce the blast radius when something goes wrong.

Use lockfiles. Review dependency changes. Limit secrets in CI/CD. Disable install scripts where practical. Protect developer accounts. Have a plan for when a package is compromised.

The basics still do a lot of heavy lifting when they are implemented well.

If you are not sure how exposed your business is to software supply chain risk, start with a practical review of your repositories, CI/CD pipelines, developer access, and secrets management.

MN Risk Advisory can help identify where the real risks are and build a plan that fits how your business actually operates.