Stop risky package
installs before
they run.
SafeInstall wraps npm, pnpm, and bun: install policy runs first, then the real tool. Open source, MIT licensed. No account required. Eight policy checks — from release age to transitive install scripts — including Sigstore provenance verification, typo-squat detection, and provenance continuity. And for the AI-agent era: an advisory MCP server, an enforcing shell-hook guard, and the Agent Trust Surface— so an agent can't quietly rewrite the rules that protect you.
Global install
One-off / CI
Node >=20 · Open source · MIT license
Only 3 runtime dependencies · Sigstore and MCP load on demand
Package install is now an attack surface.
Supply-chain attacks through npm and similar registries are increasing. The way developers install packages has changed. The tooling has not kept up.
Package installs are an attack surface.
Lifecycle scripts run arbitrary code at install time. A single malicious dependency can exfiltrate tokens, modify files, or establish persistence — before your app even starts.
AI coding makes this worse.
When an AI assistant suggests a package, most developers install it immediately. There is no pause for research, no audit, no second opinion. The speed that makes AI coding powerful also removes the friction that used to catch bad packages.
Scanners alert after the fact.
npm audit and most security tools tell you about known vulnerabilities in packages you already installed. They do nothing about install-time script execution. They do not prevent the package from running during install.
The fix is not more dashboards.
Developers need a guardrail that blocks many risky installs before they run—policy first, then the package manager. Not only a report after the fact.
Valid signatures are not enough.
A compromised npm maintainer can publish a malicious version that cryptographically verifies — signed by a GitHub Actions workflow the attacker controls in a fork of the real repository. Scanners and signature-only checks accept it. Install-time policy with trusted publisher pinning refuses it.
Eight protections. Four on by default, four opt-in.
SafeInstall evaluates policy before your package manager runs. Four checks run by default. Typo-squat detection, Sigstore provenance verification, transitive dependency evaluation, and provenance continuity are opt-in per project via safeinstall.config.json.
Fresh registry releases
By default, registry versions newer than 72 hours are blocked (configurable via minimumReleaseAgeHours). That window is where many supply-chain publishes get noticed late.
Lifecycle scripts
Packages with preinstall, install, or postinstall scripts are blocked unless you allow those scripts per package in allowedScripts. SafeInstall still forwards --ignore-scripts to the package manager by default so installs stay non-blind.
Non-registry sources
Git, tarball, and direct URL installs are blocked unless that source type is in allowedSources. Registry, workspace, file, and directory sources are allowed by default.
Trust downgrades
Two cases: a registry dependency moves to git/url/tarball, or a new registry version introduces lifecycle scripts where the installed version had none (compared using local node_modules when present).
Typo-squat detection
Requested package names are compared against a curated list of popular npm packages via Damerau-Levenshtein distance with transposition. Close-but-not-exact matches like raect, lodsh, and axois are flagged. Opt-in via typoSquat.mode = 'warn' or 'block'. Minimum name length and per-project ignore list are configurable.
Provenance verification
Fetches the Sigstore attestation bundle for registry packages, verifies signatures against the public Sigstore trust root, checks Rekor transparency log inclusion, and extracts the source repository from the SLSA v1 provenance statement. Pin the expected owner/repo per package via trustedPublishers. Mismatches always block, even in warn mode, because a valid signature from the wrong source is what maintainer-account compromise looks like.
Transitive dependencies
Walks the full lockfile tree and flags transitive packages that declare lifecycle scripts or resolve from untrusted sources. Zero extra registry calls — reads directly from pnpm-lock.yaml or package-lock.json. The ua-parser-js attack class: a deeply nested dependency running code at install time. Opt-in via transitive.mode = 'warn' or 'block'.
Provenance continuity
Learns a per-package trust baseline from the provenance identity of recent versions, then blocks deviations: a version that arrives with no attestation where recent ones had it (downgrade), or one attested from a different source repository (identity discontinuity). The fingerprint of an account-compromise publish. Packages that never adopted provenance simply have no baseline, so the check stays silent — no false positives. Reads npm's attestation metadata, so it works without the optional sigstore package. Opt-in via continuity.mode = 'warn' or 'block'.
Catching a maintainer-compromise attack.
A valid Sigstore signature is not enough. An attacker who compromises an npm maintainer account can publish a malicious version of a package you already trust, and the attestation on that malicious version will cryptographically verify — signed by a GitHub Actions workflow the attacker controls in a fork of the real repository. SafeInstall catches this. Pin the expected source repository with provenance.trustedPublishers and any build that comes from anywhere else is blocked, even if the signature is valid.
This is the only install-time policy gate that enforces the source-repository property of an npm provenance attestation. CVE scanners look for known vulnerabilities. Content analyzers look for suspicious code. SafeInstall verifies that the cryptographic chain of trust points at the repository you agreed to trust — and refuses anything else.
Pinning is the strongest form, but it needs you to declare the expected repository up front. Provenance continuity (new in 0.7.0) closes the gap for everything you have not pinned: it learns a per-package baseline from the provenance identity of recent versions and blocks the deviations npm itself does not — a version that suddenly arrives with no attestation (the fingerprint of a publish from a stolen token) or one attested from a different repositorythan the established baseline. It reads npm's published attestation metadata, so it works without the optional sigstore package. Packages that never adopted provenance have no baseline and the check stays silent — no false positives, no global “require provenance” sledgehammer. Opt in with continuity.mode.
SafeInstall verifies its own Sigstore attestation against its own GitHub repository via the public Sigstore transparency log.
Evaluate first. Execute second.
No registry proxy. No middleware on downloads. Policy runs locally, then the underlying tool runs as usual.
You run SafeInstall
Prefix your package manager command with safeinstall.
SafeInstall evaluates
For registry packages it uses public npm registry metadata (publish time, scripts). Project installs for pnpm/npm use the lockfile so versions match what the repo will install.
Block or allow
Policy violations exit with code 2 and print Blocked: … lines. Clean runs print Allowed: policy checks passed. then invoke the manager.
Package manager runs
On allow, your exact command runs. SafeInstall appends defaults like --ignore-scripts per packageManagerDefaults unless you change them.
Scanners report. SafeInstall runs first.
npm audit and similar tools report on known issues in dependencies you already installed. SafeInstall evaluates policy before the package manager: it can refuse installs that violate age, script, or source rules, and it forwards --ignore-scripts by default unless you change defaults — see docs for behavior.
| — | Scanners (npm audit, Snyk…) | SafeInstall |
|---|---|---|
| When it runs | After install | Before install |
| What it checks | Known CVEs in installed packages | Install policy: age, scripts, source |
| Output | Vulnerability report | Allow or block, with Blocked: … lines |
| Lifecycle scripts | Does not block install | Blocks if unallowed; default forwards --ignore-scripts to PM |
| Fresh release window | No | Yes (default 72h minimum age) |
| Registry metadata | Varies | Uses npm registry API when evaluating registry packages |
| Requires security expertise | To act on results: yes | Readable block reasons |
| Maintainer-compromise detection | No | Yes (via trusted publisher pinning) |
| Provenance verification | No | Yes (via Sigstore public trust root) |
| Typo-squat detection | No | Yes (embedded top-N list, Damerau-Levenshtein) |
| Transitive dependency checks | Limited (post-install only) | Yes (lockfile tree walk, zero registry calls) |
| GitHub Action for CI | No (manual setup) | Yes (Mickdownunder/SafeInstall@v1) |
Use both. They solve different problems. SafeInstall is pre-install; scanners are post-install.
Vibe coding is fast.
Blind installs are the cost.
SafeInstall is the gate.
AI assistants 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 adds one layer between suggestion and execution: policy. Release age, lifecycle scripts, source type, trust signals — checked before the package manager runs. You keep the speed. You lose the blind spot.
If you vibe code, this is the safety net your workflow is missing.
The MCP server (0.8.0) lets the agent consult the policy engine — point Claude Code, Cursor, Windsurf, or Cline at it and add the agent rule, and check_package is called before any install is suggested.
Enforcement (0.9.0). safeinstall guard install hooks Claude Code and Cursor at the shell layer. Every command the agent runs is intercepted before execution: raw installs are denied with the SafeInstall-routed command to run instead, package runners like npx require your approval — whether or not the agent cooperates.
Self-defending (0.10.0). The guard stops installs — the Agent Trust Surface stops the agent from rewriting the guard. It locks the files that configure SafeInstall and your agent, and re-verifies them in CI.
Works with every AI tool that suggests or runs installs
Typical vibe coding workflow
Enforce it — one command, per project
Registers a pre-execution shell hook for Claude Code and Cursor. Denied installs come back with the exact SafeInstall-routed command, so the agent self-corrects in one step. Guard docs → · MCP setup →
Your agent's config files are the new crontab.
A prompt-injected agent doesn't have to fight the guard — it can just rewrite the rules: weaken safeinstall.config.json, delete the hook, plant an invisible instruction in CLAUDE.md, or register a malicious MCP server. Those files program the next session. In the agent era, they are where persistence lives.
safeinstall trust lock records a hash baseline of that surface. SafeInstall reconciles the real state against it before every guard decision and every install — so tampering shows up as drift, even a bypass the guard never sees.
No SafeInstall tool at the market does this. Every other guard asks “is this package bad?” The trust surface asks the question the agent era added: did anyone quietly change the rules?
Lock it, and wire the CI anchor
The generated workflow re-hashes the committed baseline on a machine the agent does not control — the durable anchor. Trust Surface docs →
Three zones, proportionate responses
Honest scope. Locally this is tamper-evident against mistakes and non-targeted tampering, not tamper-proof against a scheme-aware agent in your own account. The durable guarantee is CI re-verifying the committed lock — which is exactly why trust lock --ci scaffolds it for you.
Same commands — SafeInstall in front.
Also supported: safeinstall init, safeinstall --json …, and project installs that resolve direct deps from lockfiles (pnpm / npm).
Five lines of YAML. Policy on every pull request.
SafeInstall ships as a reusable GitHub Action. Add it to any workflow — no CLI installation, no configuration required. Defaults apply immediately. Blocked dependencies appear in the GitHub Actions job summary with the exact block reason.
Supports check mode (audit direct dependencies without installing) and install mode (enforce policy during the actual install step). Pin a specific CLI version for reproducible builds, or let it track latest.
Built for developers who ship fast.
A CLI guardrail, not a platform. Scope is intentional: pre-install policy only.
Your machine, your responsibility.
- —AI-assisted workflows mean more installs and less package review
- —No security team to catch risky publishes for you
- —One global install — policy runs locally, no account needed
- —Use safeinstall check on direct dependencies before you ship
Move fast. Don't run installs blind.
- —Small teams ship quickly; one bad install can hit everyone
- —Commit safeinstall.config.json so policy matches across machines
- —Exit code 2 on block is CI-friendly when you wrap installs in pipelines
- —pnpm and npm project installs honor lockfiles — fewer surprise versions
- —Add the GitHub Action to your CI pipeline — policy runs on every pull request without developers needing to remember a CLI prefix
- —Enable transitive.mode to catch risky install scripts buried deep in the dependency tree — the attack surface npm audit misses
Your AI writes the install command. You control the gate.
- —Using Cursor, Copilot, Cline, Claude Code, or Windsurf
- —AI suggestions skip the usual review instincts — SafeInstall does not
- —Same prefix (safeinstall) for npm, pnpm, and bun
- —JSON mode (--json) fits automation around agents and scripts
- —GitHub Action catches risky installs even when the AI commits directly to a branch without human review
Free forever. Open source.
Install in 10 seconds.
Install the CLI globally or try it with npx. No account, no sign-up, no registry auth. Read the docs · View source
Add to any GitHub Actions workflow · Node >=20 · MIT license