SafeInstall
Stop risky package installs before they run.
Local-first CLI wrapper for npm, pnpm, and bun.
Policy runs first. Then your package manager. Not the other way around.
Open source · MIT licensed · Free forever
AI coding tools suggest packages in seconds. They don't check publish dates. They don't read install scripts. They don't verify the source. You type "yes" and move on.
SafeInstall is the gate between suggestion and execution.
$ safeinstall pnpm add compromised-pkg@9.9.9
Install blocked.
- compromised-pkg@9.9.9
Blocked: release too new (published 3 hours ago; minimum is 72 hours).
Blocked: install script present (has postinstall).
No dashboard. No account. No cloud. One command prefix — policy runs locally, blocks by default, then invokes the real tool.
npm install -g safeinstall-cliNode.js >=20 · macOS, Linux, Windows · Command:
safeinstall
safeinstall init # create safeinstall.config.json
safeinstall pnpm add axios # policy runs, then pnpm
safeinstall npm install # lockfile-aware project install
safeinstall check # audit direct deps against policy┌─────────────────────┐ ┌──────────────┐ ┌─────────────────┐
│ safeinstall pnpm │ ──▶ │ Resolve & │ ──▶ │ Policy check │
│ add axios │ │ fetch meta │ │ (age, scripts, │
└─────────────────────┘ └──────────────┘ │ sources, ...) │
└────────┬────────┘
│
┌─────────▼─────────┐
pass → │ Invoke pnpm add │
fail → │ Exit 2 (blocked) │
└───────────────────┘
- Resolves what would be installed
- Fetches registry metadata (publish time, declared scripts)
- Evaluates policy rules
- Blocks (exit 2) or invokes the real package manager
No registry proxy. No tarball rewriting. No cloud dependency.
| Rule | Default | Block message |
|---|---|---|
| Release age | 72 hours minimum | Blocked: release too new |
| Lifecycle scripts | preinstall, install, postinstall blocked | Blocked: install script present |
| Source types | registry, workspace, file, directory allowed | Blocked: untrusted source |
| Trust downgrade | Detects registry→git/url or new scripts on update | Blocked: trust level dropped |
All rules are configurable. Ambiguous or incomplete metadata blocks instead of allowing.
# Ad-hoc installs
safeinstall pnpm add axios
safeinstall npm install react@19.2.0
safeinstall bun add zod
# Project installs (lockfile-aware for npm/pnpm)
safeinstall pnpm install
safeinstall npm ci
# Monorepo — target one package
safeinstall pnpm -C packages/app install
safeinstall npm --prefix packages/app ci
# Utilities
safeinstall check # direct dependency audit
safeinstall check --json # machine-readable
safeinstall init # create starter config
safeinstall init --force # overwrite existing config
safeinstall --help
safeinstall --version
# JSON output (CI/automation)
safeinstall --json pnpm add axiosFor pnpm install and npm install / npm ci, dependency versions come from the lockfile — not loose ranges in package.json.
pnpm-lock.yamlfor pnpmpackage-lock.jsonornpm-shrinkwrap.jsonfor npm- Stale, missing, or mismatched lockfile entries fail closed
- If
packageManageris set inpackage.json, using a different CLI is blocked - Workspace-targeting flags (
--filter,--workspace) are blocked — use-Cor--prefix bun installuses manifest-oriented analysis (full lockfile parity not yet implemented)
Optional safeinstall.config.json — discovered by walking upward from the project directory.
{
"minimumReleaseAgeHours": 72,
"registryUrl": "https://registry.npmjs.org",
"allowedScripts": {
"esbuild": ["postinstall"]
},
"allowedSources": ["registry", "workspace", "file", "directory"],
"allowedPackages": [],
"ciMode": false,
"packageManagerDefaults": {
"npm": { "ignoreScripts": true },
"pnpm": { "ignoreScripts": true },
"bun": { "ignoreScripts": true }
}
}| Field | Purpose |
|---|---|
minimumReleaseAgeHours |
Minimum age in hours for registry versions |
registryUrl |
npm-compatible registry URL for metadata (mirrors, Artifactory, Verdaccio) |
allowedScripts |
Per-package lifecycle script exceptions |
allowedSources |
Permitted source types |
allowedPackages |
Names that skip policy entirely (with warning) |
ciMode |
Reserved for future CI-specific behavior |
packageManagerDefaults |
Per-manager flags forwarded to the tool |
Run safeinstall init to generate a starter config.
| Code | Meaning |
|---|---|
0 |
Allowed / check passed |
1 |
Runtime or config error |
2 |
Blocked by policy |
Use exit code 2 like any other failing step in a CI pipeline.
Pass --json anywhere in the command. Structured output goes to stdout.
safeinstall --json pnpm add axiosFields: command, commandString, packageManager, decision, summary, reasons, warnings, affectedPackages, exitCode, exitCodeMeaning. Allowed installs include execution.stdout and execution.stderr.
Fresh release blocked
$ safeinstall pnpm add axios
Using config: built-in defaults
Install blocked.
- axios@1.14.0
Blocked: release too new (axios@1.14.0 is 6 hours old; minimum is 72 hours).
Suggestion: Retry later or lower minimumReleaseAgeHours if this package is intentionally urgent.
Git source blocked
$ safeinstall npm install github:axios/axios
Using config: built-in defaults
Install blocked.
- github:axios/axios
Blocked: untrusted source (git).
Suggestion: Use a registry release or allow this source intentionally.
Package manager mismatch
$ safeinstall npm install
Using config: built-in defaults
Install blocked.
- Project install blocked: package.json declares pnpm as packageManager, but this command uses npm.
Stale lockfile
$ safeinstall pnpm install
Using config: built-in defaults
Install blocked.
- Project install blocked: axios is declared in package.json but missing from pnpm-lock.yaml.
- Not a CVE scanner — pair with
npm auditor Snyk for vulnerability data - Transitive dependencies not fully evaluated yet
peerDependenciesnot evaluated unless also declared as direct dependencies- Trust downgrade detection requires prior install state in
node_modules bun installuses manifest-only analysis (lockfile parity not yet implemented)safeinstall checkevaluates direct dependencies only- Ambiguous metadata blocks instead of guessing — by design
- Vulnerability scanning or CVE databases
- Registry proxying or tarball rewriting
- Malware detection or provenance attestation
- Selective lifecycle script execution (forwards
--ignore-scriptsby default)
SafeInstall works with any tool that runs package manager commands — including AI coding assistants:
Cursor · GitHub Copilot · Cline · Claude Code · Windsurf · Aider · Devin · Continue
Just prefix your install commands with safeinstall. Same workflow, one safety layer.
pnpm install
pnpm typecheck
pnpm test
pnpm buildIssues and PRs welcome. Author merges at own discretion — this is a solo-maintained project.
MIT — see LICENSE.
SafeInstall is provided as-is under the MIT license. It is a policy tool that enforces configurable rules on package installs. It does not guarantee the safety of any package, does not detect all supply-chain attacks, and does not replace professional security review. Use at your own risk. The authors are not liable for any damages arising from the use of this software.
safeinstall.dev · npm · GitHub