pmharden
v0.1.5
Published
Security hardening CLI for npm, pnpm, yarn, and bun — config linter, global package auditor, and .npmrc secret scanner
Maintainers
Readme
pmharden
Audits the environment your npm/pnpm/yarn/bun installs run in — not just the packages.
npx pmhardenThe problem
I ran this on my own machine and found a plaintext npm token in ~/.npmrc — readable by any postinstall script on any package I'd ever installed.
That's the same attack that hit eslint-scope in 2018. A compromised maintainer account pushed a malicious version. The postinstall script read ~/.npmrc, sent the auth token to an attacker server, then used it to publish more malicious packages. Self-propagating.
The token had been sitting there for years. npm audit never flagged it. Socket.dev never flagged it. Neither tool looks at your config files.
What it does
Three checks, one command:
pmharden audit — config linter (.npmrc, .pnpmrc, .yarnrc.yml, bunfig.toml)
pmharden secrets — plaintext tokens, file permissions
pmharden global — stale or risky globally installed packages
pmharden — all threeExample output:
Config Audit
✖ CRITICAL [npm] ~/.npmrc (plaintext-npm-token)
Plaintext secret: //registry.npmjs.org/:_authToken=npm_***REDACTED***
Fix: Replace with ${NPM_TOKEN}
✖ HIGH [npm] ~/.npmrc (allow-git-all)
allow-git=all permits installs from unreviewed git commits.
Fix: npm config set allow-git=none
✖ HIGH [yarn] ~/.yarnrc (yarn-v1-no-script-control)
yarn v1 has no config-level script blocking. Every postinstall attack
applies with no mitigation path.
Fix: yarn set version berry
─── Summary ────────────────────────────────
1 critical 2 high 1 medium
Action required: Fix critical/high issues before your next install.
⚡ Fix all with one command:
claude -p 'You are fixing package manager security issues...'
opencode run '...'When it finds issues, it generates a single prompt you can paste into Claude or OpenCode to fix everything in one shot.
Why existing tools don't catch this
| Tool | What it checks | What it misses |
|------|---------------|----------------|
| npm audit | CVEs in installed packages | Your config, your secrets, your globals |
| Socket.dev | Per-package behavior analysis | Config files, ~/.npmrc tokens |
| npq | Install-time interception | Existing misconfig risks |
| Snyk | Dependency CVEs | PM environment audit |
| pmharden | Config + secrets + globals | Per-package CVE database |
Use it alongside npm audit, not instead of it.
The attacks this prevents
postinstall execution
Every package you install can run arbitrary code during npm install unless ignore-scripts=true is set.
| Attack | Year | What happened |
|--------|------|---------------|
| eslint-scope | 2018 | Malicious postinstall read ~/.npmrc tokens, published more malicious packages |
| ua-parser-js | 2021 | Hijacked account, preinstall dropped crypto miners on millions of machines |
| node-ipc | 2022 | Maintainer added postinstall that wiped files on Russian/Belarusian IPs (CVE-2022-23812) |
| Ledger Connect Kit | 2023 | Malicious build scripts drained crypto wallets |
pmharden audit checks that ignore-scripts=true is set.
zero-day window
Malicious packages often get reported and pulled within days. If you install the day they're published, you're in the window.
| Attack | Year | Notes | |--------|------|-------| | crossenv + 36 typosquats | 2017 | 36 packages, all immediately malicious | | IconBurst | 2022 | Typosquat packages scraped form data from live apps | | LofyGang | 2022 | 200+ packages, exfiltrated Discord tokens |
pmharden audit checks that minimum-release-age=7 days is set — blocks packages published in the last 7 days.
plaintext tokens
GitGuardian's annual report consistently puts npm tokens among the top leaked secrets in public repos. Once a postinstall script has your publish token, it can push new versions of any package you own.
pmharden secrets checks for plaintext tokens and overly-permissive file modes.
Config checks
npm
| Setting | Risk | Severity |
|---------|------|----------|
| ignore-scripts not true | Allows postinstall execution | HIGH |
| allow-git=all | Installs from unreviewed git commits | HIGH |
| No minimum-release-age | Zero-day window open | MEDIUM |
| unsafe-perm=true | Scripts run as root | HIGH |
| audit=false | Disables built-in CVE checks | MEDIUM |
pnpm
| Setting | Risk | Severity |
|---------|------|----------|
| strict-dep-builds not set | Build scripts run without review | HIGH |
| No minimumReleaseAge | Zero-day window open | MEDIUM |
| blockExoticSubdeps not set | Non-registry sub-dependencies allowed | MEDIUM |
yarn
| Setting | Risk | Severity |
|---------|------|----------|
| yarn v1 (classic) | No config-level script blocking — architectural gap | HIGH |
| enableScripts: false missing (v2+) | Allows postinstall execution | HIGH |
yarn v1 has no ignore-scripts equivalent that works via .yarnrc. See yarnpkg/yarn#5335. Every attack in the table above hit yarn v1 users with no mitigation available.
Install
npm install -g pmharden # global
npx pmharden # no installCI
- name: pmharden self-audit
run: node dist/cli.js audit # or: npx pmharden@latest auditExit code 1 on CRITICAL/HIGH. Suitable for blocking merges.
Contributing
Good first issues:
binding.gypdetection — pure-JS packages with native build files (typosquat signal)- Token scope checking — distinguish publish tokens from read-only tokens
- pnpm
onlyBuiltDependenciesallowlist enforcement - Bun lockfile integrity checks
--fixflag — auto-apply safe remediations to config files
See AGENTS.md for the full contribution guide including how to add a new check.
Further reading
- OWASP npm Security Cheat Sheet
- Dependency Confusion — Alex Birsan, 2021
- State of Secrets Sprawl — GitGuardian annual report
- pnpm security options
- npm provenance
MIT
