Automating npm Package Security Checks with npq
npq audits an npm package before it installs, while npm audit only reports vulnerabilities after the code is already written to node_modules. That ordering is the entire point: by the time anyone audits the dependency tree, a malicious postinstall script has already executed on your machine. npq moves the check upstream — it inspects package metadata, age, scripts, and known CVEs at the install request, then asks whether you want to continue.
This guide wires npq into your terminal and a pre-commit hook, so the audit runs where you pull dependencies — not just when you remember to run it.
npm install -g npq
Key Takeaways
npm auditruns against packages already written tonode_modules; npq intercepts the install request before any code touches your disk.npq-herois the drop-in npm wrapper installed alongside npq; aliasnpmtonpq-hero(not tonpqdirectly) to preserve all npm subcommand passthrough behavior.- By default npq waits 15 seconds and then auto-continues the install when only warnings are present — set
NPQ_DISABLE_AUTO_CONTINUE=trueor pass--disable-auto-continueto replace the countdown with an explicit(y/N)prompt. This is interactive review, not a hands-off CI gate. - Running bare
npq(noinstallsubcommand) in a project directory audits every dependency inpackage.json— useful before opening a pull request that adds or upgrades dependencies. - npq’s marshalls are heuristic signals, not proof of malice — a package published yesterday with zero downloads trips npq’s age and popularity checks, but every legitimate new package starts there.
The gap npq fills
npq is a pre-install security auditor that runs a set of checks — called “marshalls” — against a package’s metadata before npm fetches and executes any of its code. This is the inverse of npm audit, which reads your installed dependency tree and reports CVEs after the fact. The 2025 Shai-Hulud campaign weaponized lifecycle scripts that execute during install, which is exactly the window npm audit can’t cover and npq can. The tool is open source at lirantal/npq and authored by Liran Tal.
Install npq and run a one-off audit
Discover how at OpenReplay.com.
Install npq globally, then call npq install in place of npm install for any package you want vetted first.
npm install -g npq
npq install express
The npq package on npm installs two binaries: npq and npq-hero. The first runs an explicit audit; the second is a drop-in npm wrapper covered in the next section. When you run npq install express, npq fetches metadata, runs its marshalls, prints any findings, and — if something is flagged — prompts you before delegating to npm.
A flagged package produces output along these lines:
warning Package has install scripts (preinstall, postinstall)
warning Package is older than 22 days? no — published 1 day ago
? Would you like to continue installing package(s)? (Y/n)
The prompt is the control point. Nothing installs until you answer, or the 15-second warning-only auto-continue timer expires.
Alias npm to npq-hero and persist it
To audit every install automatically, alias npm to npq-hero and write the alias into your shell config. npq-hero is the wrapper designed for this — it passes through every npm subcommand (run, ci, publish, and the rest) unchanged, only intercepting installs to run the audit first.
echo "alias npm='npq-hero'" >> ~/.zshrc # or ~/.bashrc for bash
source ~/.zshrc
With the alias in place, npm install fastify routes through npq’s marshalls before npm ever fetches the tarball. Alias npm to npq-hero rather than to npq directly — npq is the audit binary, while npq-hero is the full npm-compatible passthrough wrapper documented in the npq README. Aliasing to the raw npq binary breaks subcommands that npq doesn’t itself implement.
What does npq actually check?
npq runs a set of security checks, called “marshalls”, which are documented in the npq README. They fall into four practical categories:
| Category | What it flags |
|---|---|
| Supply chain signals | Known vulnerabilities (via OSV, or Snyk when configured), registry signature verification, build provenance attestation |
| Malware indicators | Presence of preinstall/postinstall scripts, typosquatting look-alikes of popular packages, new binary/CLI introduction |
| Package health | Missing README, LICENSE, or repository URL; deprecation signals; download counts |
| Maintainer signals | Maintainer count and contact details, expired or invalid maintainer domains, package age (new-package detection) and version maturity |
Grouping them this way maps to how an attack actually surfaces: a typosquat with a postinstall script and zero downloads trips multiple marshalls at once. Each check is a signal you weigh, not a binary verdict — npq surfaces the data and hands you the decision.
Disable auto-continue for explicit review
By default, npq’s interactive prompt waits 15 seconds and then auto-continues the install when only warnings are present. Set NPQ_DISABLE_AUTO_CONTINUE=true or pass --disable-auto-continue to remove the countdown and force an explicit (y/N) answer before npq hands off to npm. Both the flag and the environment variable are documented in the npq README.
# Either form works
NPQ_DISABLE_AUTO_CONTINUE=true npq install
npq install --disable-auto-continue
This makes npq a deliberate review step — useful at your terminal and inside a git commit hook, where someone is there to answer the prompt. It is not a documented non-interactive failure mode: the prompt still expects an answer, and npq does not advertise a flag that converts every flagged package into an automatic non-zero exit.
Route pnpm through npq
npq delegates to the package manager named in NPQ_PKG_MGR, so the same audit layer covers pnpm. Set the variable per-command for one-offs, or bake it into a shell alias for daily use.
# One-off audits through pnpm
NPQ_PKG_MGR=pnpm npq install fastify
# Make pnpm always go through npq
alias pnpm="NPQ_PKG_MGR=pnpm npq-hero"
echo 'alias pnpm="NPQ_PKG_MGR=pnpm npq-hero"' >> ~/.zshrc
The NPQ_PKG_MGR variable, listed in the npq README, tells npq-hero which package manager to invoke after the audit passes. The aliased form gives you the same guarantee for pnpm that the npm alias gives you for npm: no install proceeds until the marshalls run.
Add a husky pre-commit hook
Catch risky dependencies before they’re committed by running npq from a husky pre-commit hook. This audits the dependencies declared in package.json whenever you commit, which is most valuable on commits that touch dependency manifests.
Install husky and initialize it (husky v9 syntax):
npm install --save-dev husky
npx husky init
Then write the hook to .husky/pre-commit:
# .husky/pre-commit
if git diff --cached --name-only | grep -qE 'package(-lock)?\.json'; then
npx npq
fi
The git diff --cached check scopes the audit to commits that actually change package.json or package-lock.json, so unrelated commits don’t pay the audit cost. Bare npx npq (no install subcommand) audits the dependencies declared in package.json and exits non-zero when issues are found. Because Husky blocks commits when a hook exits non-zero, this makes the hook a hard pre-commit gate. The npx husky init command and the bare-script .husky/pre-commit format are the v9 husky convention; v8 used a different husky add syntax, so confirm your installed version before copying older snippets.
Honest limitations: heuristics, not proof
npq’s marshalls are heuristic signals, not proof of malice. A package published yesterday with zero downloads is suspicious by npq’s age and popularity checks — but every legitimate new package starts in exactly that state. npq reduces the noise to a set of weighted signals and hands you the decision; it does not, and cannot, classify intent. Treat a flagged install as a prompt to read the package, not as a confirmed threat.
npq also doesn’t replace npm audit or Socket — it complements them. The three tools fill different windows:
| Tool | When it runs | What it checks | CI-native |
|---|---|---|---|
| npq | Before install | Age, install scripts, typosquats, known CVEs via OSV (or Snyk) | No — prompts interactively |
npm audit | After install | CVEs in already-installed packages | Yes (built-in) |
| Socket | Continuous | Behavioral analysis of the dependency graph, post-merge monitoring | Yes (app + CLI) |
npq catches pre-install signals; npm audit catches CVEs in packages already on disk; Socket adds continuous behavioral monitoring of your dependency graph after merge. Pairing npq’s install-time gate with --ignore-scripts as a default (and tools like LavaMoat allow-scripts for the dependencies that genuinely need lifecycle scripts) closes most of the install-time attack surface.
Conclusion
Aliasing npm to npq-hero and gating commits with a husky hook puts the same pre-install audit in front of every place you pull dependencies — local terminal and commit. Start with the global install and the shell alias, then wire up the husky hook the next time you touch your dependency manifests. The marshalls are heuristics, so read what they flag — but reading a warning beats discovering a postinstall payload after it has already run.
FAQs
Not in a way the project documents. Setting NPQ_DISABLE_AUTO_CONTINUE=true or passing --disable-auto-continue removes the 15-second auto-continue and forces an explicit (y/N) prompt, but the prompt still expects an answer. There is no documented flag that turns every flagged package into an automatic non-zero exit. npq's reliable use cases are interactive: at your terminal via the npq-hero alias, and at commit time via a husky hook, where you are there to answer the prompt.
npq and npq-hero are two separate binaries installed together. npq is the explicit audit command you run in place of npm install. npq-hero is the full npm-compatible wrapper: it passes through every npm subcommand such as run, ci, and publish unchanged, intercepting only installs to run the audit first. Aliasing npm to the raw npq binary breaks subcommands npq does not implement, so the README recommends aliasing to npq-hero.
npq works with pnpm through the NPQ_PKG_MGR environment variable. Setting NPQ_PKG_MGR to pnpm tells npq-hero which package manager to invoke after the audit passes. You can set it per command, as in NPQ_PKG_MGR=pnpm npq install fastify, or bake it into a shell alias such as alias pnpm='NPQ_PKG_MGR=pnpm npq-hero' so every pnpm install routes through npq's marshalls first.
Use both, because they cover different windows. npq runs before install and inspects package age, lifecycle scripts, typosquatting, and known vulnerability data via OSV (or Snyk when configured), stopping malicious code before it touches disk. npm audit runs after install and reports CVEs in packages already written to node_modules. npq catches the install-time attack surface that npm audit cannot, while npm audit remains useful for ongoing CVE reporting on installed dependencies.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.