Safeguard
Open Source

npm Commands: A Security-Focused Guide to Safe Usage

The npm commands you run every day have security consequences. Here is a practical npm commands list with the safe way to use each one.

Aisha Rahman
Security Analyst
5 min read

The npm commands you run every day, install, ci, audit, publish, all have security consequences, and the safe way to use them is different from the default way most tutorials show. This guide is a practical npm commands list organized by the risk each one carries, so you can keep the workflow you already have while closing the gaps attackers rely on. If you have ever run npm install from a README without thinking, this is for you.

Installing: npm install vs npm ci

npm install reads package.json, resolves versions, and can update package-lock.json. That flexibility is a liability in CI, because the resolved versions can drift between runs and a freshly published malicious patch version can slip in.

npm ci is the safer command for automated builds. It installs exactly what package-lock.json pins, fails if the lockfile and package.json disagree, and never mutates the lockfile:

# In CI, always:
npm ci

# Locally, when intentionally changing dependencies:
npm install some-package

Add --ignore-scripts when you install packages you have not vetted. Lifecycle scripts (preinstall, postinstall) run arbitrary code on your machine during install, and that is a primary vector for supply chain attacks:

npm install --ignore-scripts

You can make this the default in .npmrc with ignore-scripts=true, then run trusted build scripts explicitly.

Auditing: npm audit and its limits

npm audit checks your installed tree against the npm advisory database:

npm audit
npm audit --production   # ignore devDependencies

npm audit fix will upgrade within your semver ranges automatically. Be careful with npm audit fix --force, which can install major-version bumps and break your build; review what it wants to do first.

Two honest limitations. First, npm audit only knows about vulnerabilities in the npm advisory feed, so coverage is not complete. Second, it reports known CVEs but does nothing about malicious packages that have not yet been flagged, such as a typosquat or a freshly compromised package. For those, a dedicated SCA tool that watches for suspicious install behavior and malware indicators catches things npm audit will not.

Inspecting before you trust

The most useful npm commands for security are the ones that let you look before you leap. Inspect a package without installing it:

npm view express            # metadata, maintainers, versions
npm view express dependencies
npm pack express            # download the tarball to inspect locally

Check what a project would install and where versions come from:

npm ls                      # print the installed dependency tree
npm ls --all                # include transitive dependencies
npm outdated                # what is behind, and by how much

npm ls is your fastest way to answer "why is this vulnerable package even here" — it shows the path from your direct dependencies down to the offending transitive one.

Publishing: guarding your own packages

If you publish to the registry, the publish-side npm commands need their own care. Turn on two-factor authentication for your account and require it for publishing:

npm profile enable-2fa auth-and-writes

Before publishing, check exactly what will be included so you do not leak a .env file or private keys:

npm publish --dry-run       # list files without publishing

Use a files allowlist in package.json or an .npmignore so only intended files ship. Prefer publishing from CI with a short-lived automation token over a long-lived personal token on a laptop, and scope tokens to the packages they need.

Configuration commands that change your risk

A few npm commands adjust the security posture of the whole environment:

npm config get registry     # confirm you are talking to the real registry
npm config set audit-level high
npm config set fund false

Confirming the registry matters more than it sounds: a misconfigured or hijacked registry setting can silently redirect your installs. In monorepos and internal setups, pin the registry per scope in .npmrc and be explicit about which scopes resolve to a private registry versus the public one, to avoid dependency-confusion attacks where an attacker publishes a public package with your internal name.

A safe daily npm commands list

Pulling it together, here is the short list worth muscle memory:

  • npm ci in CI, npm install only when changing deps on purpose.
  • npm install --ignore-scripts for anything unvetted.
  • npm audit on every build, npm audit fix reviewed, --force rarely.
  • npm ls to trace where a vulnerable package comes from.
  • npm publish --dry-run before every real publish, with 2FA on.

None of this slows you down much, and it removes the sharpest edges from the commands you already run.

FAQ

What is the difference between npm install and npm ci?

npm install resolves versions and can update package-lock.json, while npm ci installs exactly what the lockfile pins and fails on any mismatch. Use npm ci in CI for reproducible, tamper-resistant builds.

Is npm audit enough to secure my dependencies?

No. npm audit only reports known vulnerabilities in the npm advisory feed. It will not catch malicious or typosquatted packages that have not been flagged, so pair it with a tool that detects suspicious install behavior.

How do I stop install scripts from running arbitrary code?

Add --ignore-scripts to your install command, or set ignore-scripts=true in .npmrc. Lifecycle scripts run arbitrary code during install and are a common supply chain vector.

Which npm command shows why a vulnerable package is installed?

npm ls <package> prints the dependency path from your direct dependencies down to the transitive one, so you can see exactly what is pulling in the vulnerable version.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.