Zero-config install-time hardening for npm / pnpm / yarn / bun / cargo / mise / uv / bundler / aube.
npm · PyPI · bash · PowerShell
npx pmsec
npx pmsec --check
uvx pmsec
uvx pmsec --checkcurl -fsSL https://raw.githubusercontent.com/HikaruEgashira/pmsec/main/bash/pmsec \
-o /usr/local/bin/pmsec && chmod +x /usr/local/bin/pmsec
pmsec$dest = "$env:USERPROFILE\bin\pmsec.ps1"
New-Item (Split-Path $dest) -ItemType Directory -Force | Out-Null
Invoke-WebRequest -UseBasicParsing https://raw.githubusercontent.com/HikaruEgashira/pmsec/main/powershell/pmsec.ps1 -OutFile $dest
powershell -ExecutionPolicy Bypass -File $destpmsec enables the hardening bundle for every detected tool. Use --check to
verify, --disable to remove, and --doctor --json to inspect paths and
writability.
Options: --tool npm,pnpm,yarn,bun,cargo,mise,uv,bundler,aube, --days N,
--force, --json.
Bootstrap through registries that already enforce cooldowns:
npx --registry=https://registry.npmjs.org/ --min-release-age=0 pmsec --check
uvx --index https://pypi.org/simple --exclude-newer-package pmsec=2099-01-01 pmsec --check| Tool | Config | Key | Value | Purpose | Min version |
|---|---|---|---|---|---|
| npm | ~/.npmrc |
min-release-age |
1 |
1-day publish cooldown | npm >= 11.10.0 |
| npm | ~/.npmrc |
audit-level |
high |
high+ advisories fail audit/install checks | npm >= 6.4.0 |
| npm | ~/.npmrc |
allow-git |
root (none accepted) |
no transitive git deps | npm >= 11.15.0 |
| npm | ~/.npmrc |
allow-remote |
root (none accepted) |
no transitive remote tarballs | npm >= 11.15.0 |
| npm | ~/.npmrc |
allow-file |
root (none accepted) |
no transitive file: deps |
npm >= 11.15.0 |
| npm | ~/.npmrc |
allow-directory |
root (none accepted) |
no transitive local directories | npm >= 11.15.0 |
| npm | ~/.npmrc |
strict-allow-scripts |
true |
treat install-script policy violations as hard errors | npm >= 11.15.0 |
| npm | ~/.npmrc |
dangerously-allow-all-scripts |
false |
set user-level default for the install-script bypass escape hatch to disabled; project config, env vars, or CLI flags can still override | npm >= 11.16.0 |
| npm | ~/.npmrc |
allow-scripts-pin |
true |
pin script approvals to exact versions so npm install-scripts approve cannot be satisfied by a newer, potentially tampered release |
npm >= 11.15.0 |
| pnpm | ~/.config/pnpm/rc |
minimum-release-age |
1440 |
1-day publish cooldown | pnpm >= 10.6.0 |
| pnpm | ~/.config/pnpm/rc |
trust-policy |
no-downgrade |
reject weaker provenance than prior install | pnpm >= 10.21.0 |
| pnpm | ~/.config/pnpm/rc |
block-exotic-subdeps |
true |
no transitive git/tarball deps | pnpm >= 10.26.0; default >= 11 |
| pnpm | ~/.config/pnpm/rc |
strict-dep-builds |
true |
unreviewed lifecycle scripts fail install | pnpm >= 10.3.0 |
| pnpm | ~/.config/pnpm/rc |
verify-deps-before-run |
error |
abort pnpm run if lockfile is out of sync |
pnpm >= 10.12.0 |
| pnpm | ~/.config/pnpm/rc |
minimum-release-age-strict |
true |
treat minimum-release-age violations as hard errors | pnpm >= 10.12.0 |
| pnpm | ~/.config/pnpm/rc |
dangerously-allow-all-builds |
false |
close the all-builds escape hatch so that strict-dep-builds=true cannot be silently bypassed at the user level |
pnpm >= 10.9.0 |
| pnpm | ~/.config/pnpm/rc |
trust-lockfile |
false |
re-run release-age and trust-policy gates on every install even against a committed lockfile, closing the lockfile-poisoning bypass | pnpm >= 11.3.0 |
| pnpm | ~/.config/pnpm/rc |
minimum-release-age-ignore-missing-time |
false |
block packages whose registry metadata omits a publish timestamp, preventing attackers from bypassing the age gate by stripping the time field |
pnpm >= 10.6.0 |
| yarn | ~/.yarnrc.yml |
npmMinimalAgeGate |
"1d" |
1-day publish cooldown | yarn >= 4.10.0 |
| yarn | ~/.yarnrc.yml |
enableHardenedMode |
true |
re-check lockfile resolutions | yarn >= 4.0.0 |
| yarn | ~/.yarnrc.yml |
enableScripts |
false |
disable third-party lifecycle scripts | yarn >= 4.0.0; default >= 4.14.0 |
| yarn | ~/.yarnrc.yml |
approvedGitRepositories |
[] |
block all git-sourced dependencies | yarn >= 4.14.0 |
| bun | ~/.bunfig.toml |
[install].minimumReleaseAge |
86400 |
1-day publish cooldown | bun >= 1.3.0 |
| bun | ~/.bunfig.toml |
[install].ignoreScripts |
true |
disable lifecycle scripts | bun >= 1.3.0 |
| cargo | $CARGO_HOME/config.toml |
[install].minimum-release-age |
"1d" |
1-day publish cooldown | cargo >= 1.94.0 |
| mise | ~/.config/mise/config.toml |
[settings].minimum_release_age |
"1d" |
1-day release cooldown | mise >= 2026.4.22 |
| mise | ~/.config/mise/config.toml |
[settings].paranoid |
true |
always re-verify artifacts | current mise |
| mise | ~/.config/mise/config.toml |
[settings].gpg_verify |
true |
require GPG when available | current mise |
| mise | ~/.config/mise/config.toml |
[settings].github_attestations |
true |
verify GitHub attestations | mise >= 2025.12.12; default true |
| mise | ~/.config/mise/config.toml |
[settings].slsa |
true |
verify SLSA provenance | mise >= 2025.12; default true |
| mise | ~/.config/mise/config.toml |
[settings].locked_verify_provenance |
true |
re-verify provenance on every install even when lockfile has a checksum — prevents lockfile-poisoning bypass | mise >= 2026.4.4 |
| mise | ~/.config/mise/config.toml |
[settings].ruby.github_attestations |
true |
verify GitHub attestations for Ruby binaries installed via mise | mise >= 2025.12.12 |
| mise | ~/.config/mise/config.toml |
[settings].python.github_attestations |
true |
verify GitHub attestations for Python binaries installed via mise | mise >= 2026.3.18 |
| mise | ~/.config/mise/config.toml |
[settings].provenance_api_failures_fatal |
true |
fail install when attestation/SLSA API is unreachable, preventing silent provenance bypass | mise >= 2026.5.11; default true |
| mise | ~/.config/mise/config.toml |
[settings].aqua.github_attestations |
true |
verify GitHub attestations for tools installed via aqua backend | mise >= 2025.9 |
| mise | ~/.config/mise/config.toml |
[settings].aqua.cosign |
true |
verify cosign signatures for aqua-backend tools | mise >= 2026.5.1; default true |
| mise | ~/.config/mise/config.toml |
[settings].aqua.minisign |
true |
verify minisign signatures for aqua-backend tools | mise >= 2025.12; default true |
| mise | ~/.config/mise/config.toml |
[settings].aqua.slsa |
true |
verify SLSA provenance for aqua-backend tools | mise >= 2025.12; default true |
| mise | ~/.config/mise/config.toml |
[settings].github.github_attestations |
true |
verify GitHub attestations for tools fetched via github backend | mise >= 2026; default true |
| mise | ~/.config/mise/config.toml |
[settings].github.slsa |
true |
verify SLSA provenance for tools fetched via github backend | mise >= 2026; default true |
| mise | ~/.config/mise/config.toml |
[settings].node.gpg_verify |
true |
in-process GPG verification for Node.js tarballs using pure-Rust rPGP (no external gpg binary required) |
mise >= 2026.7.12 |
| mise | ~/.config/mise/config.toml |
[settings].swift.gpg_verify |
true |
in-process GPG verification for Swift tarballs using pure-Rust rPGP (no external gpg binary required) |
mise >= 2026.7.12 |
| mise | ~/.config/mise/config.toml |
[settings].not_found_system_fallback |
false |
prevents mise shims from silently falling back to same-named system PATH binaries when the required tool version is not installed — eliminates PATH-hijacking via shim fallback | mise >= 2026.8.3 |
| uv | ~/.config/uv/uv.toml |
exclude-newer |
"1 days" |
1-day publish cooldown | uv >= 0.9.17 |
| uv | ~/.config/uv/uv.toml |
index-strategy |
"first-index" |
avoid cross-index confusion | uv >= 0.1.0 |
| uv | ~/.config/uv/uv.toml |
[audit].malware-check |
true |
query OSV API for known-malicious packages before sync, blocking install of packages with MAL advisories | uv >= 0.11.31 |
| bundler | ~/.bundle/config |
BUNDLE_COOLDOWN |
"1" |
1-day gem cooldown | bundler >= 4.0.13 |
| aube | ~/.config/aube/config.toml |
minimumReleaseAge |
1440 |
1-day publish cooldown | aube >= 1.0.0 |
| aube | ~/.config/aube/config.toml |
paranoid |
true |
strict-security bundle | aube >= 1.0.0 |
pmsec has two knobs: --days N and --tool. All other values are fixed, and
re-running pmsec restores them.
Relax policy in the project that needs it, not in pmsec:
| Tool | Project config |
|---|---|
| npm | <project>/.npmrc |
| pnpm | <project>/.npmrc / pnpm-workspace.yaml |
| yarn | <project>/.yarnrc.yml |
| bun | <project>/bunfig.toml |
| cargo | <project>/.cargo/config.toml |
| mise | <project>/mise.toml |
| uv | <project>/pyproject.toml ([tool.uv]) / uv.toml |
| bundler | <project>/.bundle/config |
| aube | <project>/aube.toml |
Example:
# <project>/.npmrc
allow-file=workspacesReview checked-in tool configs before trusting an unfamiliar repo.
pmsec --check validates the user-global baseline only.
