DEV Community

Cover image for How to Secure NPM Dependencies ? A Complete Supply Chain Security Guide for API Developers
Preecha
Preecha

Posted on

How to Secure NPM Dependencies ? A Complete Supply Chain Security Guide for API Developers

TL;DR

NPM supply chain attacks surged to over 3,000 malicious packages in 2024 alone, and the March 2026 Axios compromise proved even top-10 packages aren’t safe. This guide shows API developers how to harden npm usage with lockfile enforcement, postinstall script blocking, exact version pinning, provenance checks, behavioral analysis, dependency reduction, and CI/CD monitoring.

Try Apidog today

Introduction

The Axios supply chain attack on March 31, 2026, wasn’t the first npm compromise. It won’t be the last. But with 83 million weekly downloads and a cross-platform RAT deployed through a single hijacked maintainer account, it was one of the clearest warnings the JavaScript ecosystem has received.

The problem is that the attack bypassed common “just update your dependencies” advice. The malicious code wasn’t in Axios itself. It was injected through a phantom dependency that triggered a postinstall hook. Lockfiles didn’t help if you ran npm install during the attack window. Version pinning didn’t help if you had not pinned yet.

API developers are especially exposed. Test scripts, CI/CD jobs, mock servers, and HTTP clients often pull from npm. A compromised package in that toolchain can leak API keys, database credentials, cloud tokens, and local developer secrets.

Apidog reduces one source of npm exposure by providing a built-in HTTP client for API testing, so you do not need Axios, node-fetch, or got in your API testing stack. You can try Apidog free while applying the defense layers below.

This guide walks through seven practical layers you can add to your npm workflow.

Layer 1: Enforce lockfiles

Why lockfiles matter

A lockfile records the exact version of every direct and transitive dependency installed at a given time.

Without a lockfile, npm install resolves the latest version that matches your semver range. For example:

{
  "dependencies": {
    "axios": "^1.14.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

The ^1.14.0 range allows npm to install later compatible 1.x.x versions. If a malicious 1.14.1 exists on the registry during install, npm can resolve to it.

What to do

Commit your lockfile:

  • package-lock.json for npm
  • yarn.lock for Yarn
  • pnpm-lock.yaml for pnpm
  • bun.lock for Bun

Use frozen installs in CI/CD instead of regular installs:

# npm
npm ci

# yarn
yarn install --frozen-lockfile

# pnpm
pnpm install --frozen-lockfile

# bun
bun install --frozen-lockfile
Enter fullscreen mode Exit fullscreen mode

npm ci deletes node_modules and installs strictly from package-lock.json. If package.json and the lockfile do not match, the command fails.

Add lockfile review to PRs

When a PR changes a lockfile, review it like application code.

Check for:

  • New transitive dependencies
  • Unexpected version bumps
  • Registry URL changes
  • New packages with install scripts
  • Packages with similar names to popular libraries

Behavioral analysis tools such as Socket.dev can also flag suspicious lockfile changes in pull requests.

The gap

Lockfiles protect against unexpected version resolution after the dependency has been locked. They do not protect the first install.

If you add a dependency during an attack window, the malicious package can still be written into your lockfile. That is why lockfiles are the foundation, not the full solution.

Layer 2: Disable postinstall scripts

Why this matters

Many npm supply chain attacks use lifecycle scripts:

  • preinstall
  • install
  • postinstall
  • prepare

These scripts run during dependency installation, before your app starts and before runtime monitoring can help.

The Axios attack, the ua-parser-js attack, the event-stream attack, and many others used this pattern.

Block scripts globally

Add this to your project .npmrc:

ignore-scripts=true
Enter fullscreen mode Exit fullscreen mode

Or configure it globally:

npm config set ignore-scripts true
Enter fullscreen mode Exit fullscreen mode

This prevents npm lifecycle scripts from running during package installation.

Handle packages that require scripts

Some packages need lifecycle scripts for native compilation or binary setup, such as bcrypt, sharp, or sqlite3.

Use one of these patterns.

Option 1: Install without scripts, then rebuild only trusted packages

npm ci --ignore-scripts
npm rebuild bcrypt sharp
Enter fullscreen mode Exit fullscreen mode

Option 2: Use an allowlist where supported

For npm 10+, create a .scriptsrc.json file and allow only trusted packages:

{
  "allowScripts": {
    "bcrypt": true,
    "sharp": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Option 3: Prefer prebuilt binaries

Some native packages now ship prebuilt binaries for common platforms. For example, sharp provides prebuilt binaries for many environments, which can reduce the need for postinstall compilation.

Before allowing scripts, check whether the package still needs them in your target runtime.

Watch Git-based dependencies

In January 2026, researchers disclosed six zero-day vulnerabilities called “PackageGate” affecting npm, pnpm, vlt, and Bun.

One finding showed that Git-based dependencies can carry configuration files that enable code execution even when lifecycle scripts are disabled.

If your package.json uses Git URLs, do not rely only on ignore-scripts.

Pin Git dependencies to specific commit hashes:

{
  "dependencies": {
    "some-package": "github:org/repo#8f4d3c2a1b..."
  }
}
Enter fullscreen mode Exit fullscreen mode

Also review the repository contents before adding it.

Layer 3: Pin exact versions

Stop using semver ranges for critical dependencies

By default, npm saves dependencies with a caret:

{
  "dependencies": {
    "axios": "^1.14.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

The caret means npm can resolve to later compatible versions. During an active compromise, that can be dangerous.

Use exact versions instead:

{
  "dependencies": {
    "axios": "1.14.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Configure npm to save exact versions by default:

# .npmrc
save-exact=true
save-prefix=
Enter fullscreen mode Exit fullscreen mode

Pin transitive dependencies with overrides

Direct dependencies have their own dependencies. If a transitive dependency is compromised, pinning only your direct dependencies is not enough.

For npm, use overrides:

{
  "overrides": {
    "axios": "1.14.0",
    "plain-crypto-js": "npm:empty-npm-package@1.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

For Yarn, use resolutions:

{
  "resolutions": {
    "axios": "1.14.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

For pnpm, use pnpm.overrides:

{
  "pnpm": {
    "overrides": {
      "axios": "1.14.0"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Trade-off

Exact pinning means you do not receive patch updates automatically. You must intentionally bump dependency versions.

For security-sensitive projects, this is usually the right trade-off: controlled updates are safer than automatic dependency drift.

Layer 4: Verify package provenance

What provenance proves

npm provenance attestation links a published package to its source code and build environment using Sigstore signatures in a public transparency ledger.

A package with valid provenance can prove:

  • Which source repository it was built from
  • Which CI/CD system built it
  • Which commit triggered the build

Check provenance locally or in CI

Run:

npm audit signatures
Enter fullscreen mode Exit fullscreen mode

This verifies that installed packages with attestations have valid signatures.

Packages published manually from a developer machine may not have provenance. For high-download or security-sensitive packages, lack of provenance should increase review scrutiny.

The malicious Axios versions lacked OIDC provenance binding and had no corresponding GitHub commits. Standard provenance checks would have made that suspicious.

Enable provenance for your own npm packages

If you publish packages, enable provenance in CI/CD.

Example GitHub Actions publish step:

- uses: actions/setup-node@v4
  with:
    node-version: 20
    registry-url: https://registry.npmjs.org

- run: npm publish --provenance
  env:
    NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

You can also add this to .npmrc:

provenance=true
Enter fullscreen mode Exit fullscreen mode

Limitations

Provenance is not a guarantee that code is safe.

It proves where and how a package was built. It does not prove that the source code is benign. A compromised CI/CD pipeline could still publish a malicious package with valid provenance.

Use provenance as one signal in a layered security pipeline.

Layer 5: Use behavioral analysis tools

Why vulnerability scanning is not enough

Tools like npm audit and Snyk check known vulnerability databases. They are useful, but they miss zero-day supply chain attacks before disclosure.

Behavioral tools inspect what packages do, including:

  • Network calls during install
  • File system access outside the package directory
  • Shell command execution
  • Environment variable access
  • Obfuscated code
  • Credential harvesting patterns

Socket.dev

Socket analyzes package behavior during installation and runtime.

It can flag:

  • Network requests during install
  • File writes outside expected directories
  • Shell commands
  • Suspicious access to environment variables
  • Obfuscated JavaScript

Image

Install and scan:

npm install -g @socketsecurity/cli
socket scan
Enter fullscreen mode Exit fullscreen mode

When integrated with GitHub, Socket can comment on pull requests when new dependencies introduce suspicious behavior.

In the Axios attack, the plain-crypto-js dependency would have triggered multiple suspicious signals, including obfuscated code, network access during postinstall, and file system writes outside the package directory.

Snyk

Snyk is stronger for known vulnerabilities, exploit maturity, risk scoring, and remediation guidance.

Install and test:

npm install -g snyk
snyk test
Enter fullscreen mode Exit fullscreen mode

Image

Use a layered scan

Run all three checks in CI:

# Baseline vulnerability audit
npm audit

# Behavioral analysis
socket scan

# Vulnerability management
snyk test
Enter fullscreen mode Exit fullscreen mode

Treat critical findings as build blockers.

Layer 6: Minimize your dependency surface

Every dependency is a trust decision

The most effective way to reduce npm supply chain risk is to have fewer packages to trust.

The average Node.js project can contain hundreds of transitive dependencies. Each dependency can execute code, publish updates, change maintainers, or pull in additional packages.

Audit your dependency tree

Count installed dependencies:

npm ls --all | wc -l
Enter fullscreen mode Exit fullscreen mode

Find duplicates and heavily repeated packages:

npm ls --all | sort | uniq -c | sort -rn | head -20
Enter fullscreen mode Exit fullscreen mode

For each dependency, ask:

  • Does Node.js provide this natively?
  • Does this package pull in many transitive dependencies?
  • Is it still maintained?
  • Is it needed in production or only development?
  • Can a small utility be vendored directly into the codebase?

Replace packages with native APIs where possible

Package Native alternative Available since
axios, node-fetch, got fetch global Node.js 18
uuid crypto.randomUUID() Node.js 19
dotenv --env-file flag Node.js 20.6
chalk util.styleText() Node.js 21.7
glob fs.glob() Node.js 22
path-to-regexp Native URL pattern API Node.js 23

Example: replace uuid with Node.js native crypto:

import { randomUUID } from "node:crypto";

const requestId = randomUUID();
Enter fullscreen mode Exit fullscreen mode

Example: replace node-fetch in Node.js 18+:

const response = await fetch("https://api.example.com/users");
const data = await response.json();
Enter fullscreen mode Exit fullscreen mode

Reduce API testing dependencies

API testing workflows often include:

  • HTTP client libraries
  • Assertion libraries
  • Test runners
  • Mock servers
  • Documentation generators

Each adds dependencies and transitive dependencies.

Image

Apidog consolidates API testing into one platform:

  • Built-in HTTP client
  • Visual test builder with assertions
  • Automated test scenarios
  • CI/CD integration through Apidog CLI
  • Smart mock server with dynamic responses
  • Documentation generated from API specs

Moving API testing into Apidog can reduce npm dependencies in your testing infrastructure, which means fewer packages to review and fewer install-time risks.

You can try Apidog free to consolidate your API testing stack.

Layer 7: Monitor network and runtime behavior

Block known-bad domains

After a supply chain attack, block known command-and-control infrastructure at the network level.

Example local block:

echo "0.0.0.0 sfrclak.com" | sudo tee -a /etc/hosts
Enter fullscreen mode Exit fullscreen mode

For CI/CD, use a stricter model: allow only the domains your build needs.

Typical allowed destinations:

  • npm registry
  • Git provider
  • Container registry
  • Cloud deployment target
  • Internal artifact repository

Everything else should be denied or at least audited.

Use StepSecurity Harden-Runner for GitHub Actions

StepSecurity Harden-Runner monitors GitHub Actions workflows in real time.

It provides:

  • Outbound network monitoring
  • Process execution tracking
  • File integrity monitoring
  • Alerts for anomalous behavior

Example:

- uses: step-security/harden-runner@v2
  with:
    egress-policy: audit
Enter fullscreen mode Exit fullscreen mode

For stricter builds, switch from audit mode to block mode after you have reviewed expected network calls.

- uses: step-security/harden-runner@v2
  with:
    egress-policy: block
Enter fullscreen mode Exit fullscreen mode

Monitor suspicious child processes

On developer machines and build agents, watch for unexpected processes spawned by node or npm.

Examples of suspicious child processes during install:

  • osascript on macOS
  • cscript on Windows
  • python3 on Linux
  • Shell commands that read environment variables
  • Commands that access SSH keys or cloud credentials

Endpoint detection and response tools can help identify these patterns.

Recommended .npmrc

Add a hardened .npmrc to your repository:

# Pin exact versions
save-exact=true
save-prefix=

# Disable lifecycle scripts
ignore-scripts=true

# Enable provenance for publishing
provenance=true

# Use the official registry
registry=https://registry.npmjs.org/

# Require web-based auth flow
auth-type=web

# Audit threshold
audit-level=moderate
Enter fullscreen mode Exit fullscreen mode

Commit this file so the whole team uses the same npm defaults.

CI/CD security pipeline example

Here is a GitHub Actions workflow that applies the layers in one pipeline:

name: Secure Build

on: [push, pull_request]

jobs:
  security-check:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: step-security/harden-runner@v2
        with:
          egress-policy: audit

      - uses: actions/setup-node@v4
        with:
          node-version: 22

      # Layer 1 + 2: Frozen lockfile, no lifecycle scripts
      - run: npm ci --ignore-scripts

      # Layer 3: Capture resolved dependency tree
      - run: npm ls --all > deps.txt

      # Layer 4: Check package signatures/provenance
      - run: npm audit signatures

      # Layer 5: Behavioral analysis
      - run: npx socket scan

      # Layer 5: Known vulnerability scan
      - run: npx snyk test

      # Layer 1: Baseline npm audit
      - run: npm audit --audit-level=moderate

      # Rebuild only explicitly approved native dependencies
      - run: npm rebuild sharp bcrypt
Enter fullscreen mode Exit fullscreen mode

For production use, add secrets only after install and scanning steps whenever possible. That reduces the impact of a malicious install-time script.

What’s coming next for npm security

Mandatory provenance for popular packages

npm is discussing provenance requirements for packages above certain download thresholds. This would reduce the risk of manual token-based publishing for widely used packages.

Two-person release approval

High-download packages may eventually require a second maintainer to approve releases. That would make a single compromised maintainer account less damaging.

Runtime permission scoping

Deno already restricts access to the network, file system, and environment variables unless explicitly granted. Node.js is exploring similar permission models.

When these models mature, install scripts and runtime code could be limited by explicit permissions.

Package manager convergence

pnpm’s stricter dependency isolation limits what packages can access. As npm and other package managers adopt stricter behavior, some dependency confusion and undeclared dependency issues become harder to exploit.

FAQ

What is an npm supply chain attack?

An npm supply chain attack targets your dependency chain instead of your application directly.

Attackers may:

  • Compromise maintainer accounts
  • Publish malicious package versions
  • Add malicious transitive dependencies
  • Publish typosquat packages
  • Abuse install scripts

When you install or update dependencies, malicious code can run on your machine or inside CI/CD.

Is npm audit enough?

No.

npm audit checks known vulnerability databases. It is useful for CVEs and disclosed issues, but it does not reliably catch zero-day supply chain attacks.

Use it with:

  • Lockfile enforcement
  • ignore-scripts=true
  • Exact version pinning
  • Provenance checks
  • Behavioral analysis tools
  • Network monitoring

Should I stop using npm?

No.

npm remains the largest JavaScript package ecosystem, and most packages are safe. The goal is not to avoid npm entirely. The goal is to reduce exposure and control how dependencies enter your project.

How does Apidog help reduce npm supply chain risk?

Apidog provides a built-in HTTP client, test runner, mock server, and documentation generator for API development.

That can reduce the need for npm packages such as Axios, node-fetch, Jest, Express-based mock servers, and other testing dependencies. Fewer dependencies means fewer attack vectors in your API development workflow.

What is npm package provenance?

Package provenance uses Sigstore to cryptographically link a published npm package to its source repository and CI/CD build environment.

It helps answer:

  • Where was this package built?
  • Which commit produced it?
  • Which workflow published it?

Verify it with:

npm audit signatures
Enter fullscreen mode Exit fullscreen mode

How many npm packages are malicious?

Snyk identified over 3,000 malicious npm packages in 2024. By Q4 2025, Sonatype blocked 120,612 malware attacks in a single quarter across npm, PyPI, and other registries.

Most malicious packages are low-download typosquats, but high-profile compromises like Axios show that popular packages are not immune.

What is PackageGate?

PackageGate is a set of six zero-day vulnerabilities disclosed in January 2026 affecting npm, pnpm, vlt, and Bun.

One finding showed that Git-based dependencies can carry configuration files that enable code execution even when lifecycle scripts are disabled.

If you use Git dependencies:

  • Pin them to commit hashes
  • Review repository contents
  • Avoid broad branch references like main
  • Do not rely only on ignore-scripts

Key takeaways

  • Commit lockfiles and use frozen installs such as npm ci.
  • Disable lifecycle scripts with ignore-scripts=true.
  • Pin exact versions with save-exact=true.
  • Use overrides to control transitive dependency resolution.
  • Verify package signatures with npm audit signatures.
  • Combine npm audit, Socket.dev, and Snyk in CI.
  • Replace unnecessary packages with Node.js native APIs.
  • Reduce API testing dependencies with integrated tools like Apidog.
  • Monitor CI/CD network egress with StepSecurity Harden-Runner.

Every dependency is a trust decision. Reduce the number of decisions, verify the ones that remain, and monitor what your build does at install time.

Top comments (0)