SafeInstall
Package safeinstall-cli (0.2.1). Local-first wrapper: policy is evaluated before npm, pnpm, or bun runs. Blocks risky installs by default and, opt-in, verifies the cryptographic provenance of registry packages via Sigstore trusted publisher pinning. Not a CVE scanner.
Overview
Prefix your package manager with safeinstall. The CLI resolves what would be installed, fetches registry metadata from the configured npm-compatible registry when needed (public npm registry by default), evaluates rules, then either exits with code 2 (blocked) or invokes the real tool—often with defaults such as ignore-scripts.
SafeInstall does not proxy the registry or modify tarballs. Network access is required to evaluate registry packages (publish time and declared scripts).
Installation
- Use Node.js >=20.
- Install globally or invoke with npx — no account or registry auth required.
Open source · MIT licensed · Source on GitHub
Usage
Pass through the same arguments you would use with the underlying tool.
Project installs
For pnpm install and npm install / npm ci, direct dependency versions come from pnpm-lock.yaml or package-lock.json (or shrinkwrap)—not loose ranges in package.json alone. Stale, missing, or mismatched lockfile entries fail closed instead of guessing.
If packageManager is set in package.json, using a different CLI is blocked. Workspace-targeting flags that may touch multiple packages (e.g. pnpm --filter, npm --workspace) are blocked; use -C or --prefix for a single package directory.
bun install still uses manifest-oriented analysis; full lockfile parity with npm/pnpm is not implemented yet.
Policy (defaults)
These are the main gates. Adjust or exempt via config where documented below.
Blocked: release too newRegistry packages must be at least 72 hours old by default (minimumReleaseAgeHours).
Blocked: install script presentpreinstall, install, and postinstall on the resolved registry version are blocked unless allowed per package via allowedScripts.
Blocked: untrusted sourceallowedSources defaults to registry, workspace, file, and directory. Git, tarball, and URL installs are outside the default set.
Blocked: trust level droppedBlocks when a dependency moves from registry to git/url/tarball, or when a new registry version adds lifecycle scripts that the version already in node_modules did not declare (comparison needs that prior install present).
Blocked: suspected typo-squatRequested package names compared via Damerau-Levenshtein distance with transposition against a curated top-package list. Three modes: off (default), warn, block. Configure via typoSquat.mode. See Configuration.
Blocked: publisher mismatch / attestation missing / attestation invalid / attestation unreachableCryptographically verifies Sigstore attestations for registry packages via the public Sigstore trust root and Rekor transparency log. Pins source repository via provenance.trustedPublishers. Three modes: off (default), warn, require. Configure via provenance.mode. See Configuration.
Configuration
Optional safeinstall.config.json is discovered by walking upward from the effective project directory; the nearest file wins.
| Field | Role |
|---|---|
| minimumReleaseAgeHours | Minimum age in hours for registry versions. |
| registryUrl | npm-compatible registry URL for metadata (mirrors, Artifactory, Verdaccio). |
| allowedScripts | Package name → allowed script names. Does not remove ignore-scripts unless you change packageManagerDefaults. |
| allowedSources | Source types permitted (add git, tarball, url only if you intend to allow them). |
| allowedPackages | Names that skip policy checks entirely (warning emitted). |
| ciMode | Stored in config; default merges CI="true". Install and check flows do not branch on it yet. |
| packageManagerDefaults | Per-manager flags forwarded when spawning the tool; ignoreScripts defaults to true. |
| typoSquat.mode | "off" / "warn" / "block". Controls the typo-squat check. Defaults to "off". |
| typoSquat.minNameLength | Minimum length of a requested package name to participate in the typo-squat check. Names shorter than this are skipped. Defaults to 4. |
| typoSquat.ignore | Array of package names (lowercased on load) that will never be flagged as typo-squats. Use for known legitimate lookalikes. |
| provenance.mode | "off" / "warn" / "require". Controls Sigstore provenance verification for registry packages. Defaults to "off". |
| provenance.requireFor | Array of package name patterns (exact or glob with * wildcard). Provenance is required for these packages even when mode is "warn". |
| provenance.trustedPublishers | Object mapping package name patterns to expected owner/repo slug patterns. Example: { "axios": "axios/axios", "@acme/*": "acme/*" }. A verified attestation whose source repository does not match the pin is ALWAYS blocked, even in "warn" mode. |
| provenance.offlineBehavior | "fail-closed" (default) or "allow-cached". Controls what happens when the attestation endpoint is unreachable. |
CI and exit codes
0 — success or policy allowed. 1 — runtime or config error. 2 — blocked by policy. Use 2 like any other failing step in a pipeline.
safeinstall check
Evaluates direct dependencies only. On success, stderr includes: Check passed: no direct dependency policy violations found.
safeinstall init
Writes a starter safeinstall.config.json in the current directory. Refuses if the file exists unless you pass --force.
JSON output
Put --json anywhere in argv. Structured output goes to stdout; human-oriented messages stay separate. Field details are stable in the CLI version you install.
Exceptions
allowedPackages — exact name match skips checks (with a warning). allowedScripts — per-package lifecycle phases you accept for policy; SafeInstall may still forward ignore-scripts by default via packageManagerDefaults.
Limitations
- —Not a CVE scanner — pair with npm audit or other tools for vulnerability data.
- —Transitive dependency policy is not fully implemented.
- —Trust script comparison needs the previous resolved tree under node_modules.
- —Bun project installs are not fully lockfile-aware.
- —peerDependencies are not evaluated unless also declared as direct dependencies.
- —Ambiguous metadata leads to a block, not a silent allow.
- —Typo-squat target list is curated and refreshed manually between releases; brand-new packages published in the last day may not yet appear on the list.
- —Provenance verification supports GitHub Actions trusted publishers on the public Sigstore root only. GitLab CI, self-hosted Sigstore, and other trust roots are out of scope for 0.2.x.
- —Git sources are identified by URL for allowlist and trusted-publisher purposes, not by inferred package name. This is intentional: conflating registry axios with github:any-fork/axios would be dangerous.
Out of scope
SafeInstall is a pre-install guard, not a full platform.
License
SafeInstall is open source, released under the MIT license. Source code is on GitHub. Free forever for individual use. Team features (shared policy, org-wide enforcement) are a planned commercial addition — not part of the current CLI.
Provided as-is under MIT. Not a security guarantee. SafeInstall is a policy tool. It does not guarantee the safety of any package, does not detect all supply-chain attacks, and does not replace professional security review.