Security model
npm, pnpm, and yarn all execute preinstall, install, and postinstall scripts silently
during installation. A malicious package can read your SSH keys, exfiltrate .env files, or
install a backdoor — all during a simple npm install. sandboxpm operates on a zero-trust
principle instead:
- No package has filesystem or network access during installation unless explicitly granted.
- Scripts are never executed silently — every script requires developer consent.
- Approved scripts run in ephemeral Docker containers with no access to host credentials, SSH
keys, or environment variables (beyond an explicit, minimal
envPassthroughallowlist). - All tarballs are verified against the SHA-512 published by the registry before extraction — never after.
The install-script prompt
When sandboxpm finds a package with a lifecycle script, you see exactly what it is before anything runs:
──────────────────────────────────────────────────────
⚠ 3 packages have install scripts
──────────────────────────────────────────────────────
1. esbuild@0.19.4
Type: postinstall
Script: node install.js
Inspect: https://unpkg.com/esbuild@0.19.4/install.js
Run this script? [y/N/inspect/always/never]
.sandboxpmrc's policies.onWarn / policies.onBlock settings let a team pre-decide how sandbox
warnings and blocks are handled in CI (abort, prompt, or continue), and whitelist/
blacklist entries let you pre-approve or permanently ban specific packages — see
Configuration.
Content-addressable store
Every file from every package is stored once in ~/.sandboxpm/store/, keyed by its SHA-512 hash.
When a package is installed into a project, its files are hard-linked from the store — never
duplicated across projects:
~/.sandboxpm/store/
└── sha512-{hash} ← one file, shared across all projects via hard links
project-a/node_modules/express → hard link to store
project-b/node_modules/express → same hard link, zero extra disk space
Non-flat node_modules
Only direct dependencies appear in the root node_modules/. Transitive dependencies live nested
under node_modules/.sandboxpm/. This prevents phantom dependency access: your code can only
import what you explicitly declared in package.json.
Docker sandbox
Approved scripts run through dockerode in a container that is:
- read-only, with
CapDrop: ALL - restricted by an explicit seccomp syscall allowlist (Linux) or Hyper-V isolation (Windows)
- attached to an isolated bridge network with no host credential or network access by default
A native (unsandboxed) fallback exists for sandbox-start failures or Linux-ELF/Windows-host native-addon mismatches, but it requires an explicit double-confirmation — sandboxpm never falls back to running a script unsandboxed silently. Full detail on the sandbox images and syscall policy lives in Architecture.