SafeInstall
Documentation

SafeInstall

Package safeinstall-cli (0.1.0). Local-first wrapper: policy is evaluated before npm, pnpm, or bun runs. Blocks many risky installs by default; it is not a CVE scanner and does not prove package contents.

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).

Local-first
Policy runs on your machine.
Lockfile-aware
npm/pnpm project installs use package-lock / pnpm-lock.
Fail-closed
Ambiguous or mismatched lockfile / packageManager states block.
Explicit outcomes
Exit 2 means blocked by policy.
Monorepo-safe
Workspace-wide flags blocked; target one package with -C / --prefix.
Honest scope
Not malware detection; transitive policy not complete.

Installation

  1. Use Node.js >=20.
  2. Install globally or invoke with npx — no account or registry auth required.
$npm install -g safeinstall-cli
# Without a global install:
$npx safeinstall-cli check

Open source · MIT licensed · Source on GitHub

Usage

Pass through the same arguments you would use with the underlying tool.

$safeinstall pnpm add axios
$safeinstall npm install
$safeinstall bun add zod
$safeinstall check
$safeinstall init
$safeinstall --json pnpm add axios

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.

Release ageBlocked: release too new

Registry packages must be at least 72 hours old by default (minimumReleaseAgeHours).

Lifecycle scriptsBlocked: install script present

preinstall, install, and postinstall on the resolved registry version are blocked unless allowed per package via allowedScripts.

SourcesBlocked: untrusted source

allowedSources defaults to registry, workspace, file, and directory. Git, tarball, and URL installs are outside the default set.

Trust downgradeBlocked: trust level dropped

Blocks 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).

Configuration

Optional safeinstall.config.json is discovered by walking upward from the effective project directory; the nearest file wins.

safeinstall.config.json (shape)
{
"minimumReleaseAgeHours": 72,
"allowedScripts": { "esbuild": ["postinstall"] },
"allowedSources": ["registry", "workspace", "file", "directory"],
"allowedPackages": [],
"ciMode": false,
"packageManagerDefaults": {
"npm": { "ignoreScripts": true },
"pnpm": { "ignoreScripts": true },
"bun": { "ignoreScripts": true }
}
}
FieldRole
minimumReleaseAgeHoursMinimum age in hours for registry versions.
registryUrlnpm-compatible registry URL for metadata (mirrors, Artifactory, Verdaccio).
allowedScriptsPackage name → allowed script names. Does not remove ignore-scripts unless you change packageManagerDefaults.
allowedSourcesSource types permitted (add git, tarball, url only if you intend to allow them).
allowedPackagesNames that skip policy checks entirely (warning emitted).
ciModeStored in config; default merges CI="true". Install and check flows do not branch on it yet.
packageManagerDefaultsPer-manager flags forwarded when spawning the tool; ignoreScripts defaults to true.

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.

GitHub Actions (illustrative)
- run: safeinstall pnpm install --frozen-lockfile
env:
CI: true

safeinstall check

Evaluates direct dependencies only. On success, stderr includes: Check passed: no direct dependency policy violations found.

$safeinstall check --json

safeinstall init

Writes a starter safeinstall.config.json in the current directory. Refuses if the file exists unless you pass --force.

$safeinstall init

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.

$safeinstall --json pnpm add axios

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.

{
"allowedPackages": ["internal-tooling"],
"allowedScripts": {
"esbuild": ["postinstall"]
}
}

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.

Out of scope

SafeInstall is a pre-install guard, not a full platform.

×Vulnerability databases / CVE scanning
×Registry proxy or tarball rewriting
×Hosted dashboards or cloud policy services
×Malware detection or provenance attestation
×Selective lifecycle script execution

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.