A system-wide security wrapper for npm and npx designed to protect developers from malicious packages, typosquatting, and compromised transitive dependencies.
Malicious actors frequently upload compromised packages to the NPM registry. Often, these are disguised as popular libraries (typosquatting) or are hidden deep inside the dependency tree of a legitimate package. Usually, these malicious packages are discovered and removed by the NPM security team within a few days.
However, if you happen to run npm install during that short window, your system gets compromised.
safe-npm disables the raw npm and npx commands and replaces them with sn (Safe NPM) and snx (Safe NPX).
Before installing or executing any package, safe-npm intercepts the command, maps out the entire nested dependency tree, and checks the publication date of every single package about to be installed. If any package (direct or transitive) is newer than 7 days, the installation pauses, alerts you to the exact suspicious package, and requires manual confirmation to proceed.
- Blocks Native Commands: Prevents accidental use of npm and npx system-wide.
- Deep Tree Scanning: Evaluates the exact versions of all nested dependencies, not just the top-level package.
- Interactive Prompts: When blocked via raw
npmornpx, it offers an automated prompt to execute the safe command instantly without retyping. - Controlled Concurrency: Implements a parallel worker pool (limited to 15 concurrent jobs) to prevent CPU spikes or registry rate-limiting (HTTP 429) on large projects.
- Smart Caching: Once a package version is verified, its publication date is stored locally. Subsequent checks are near-instant.
- System-Wide: Applies to all user accounts on the machine.
- Sudo-Proof: Because the scripts sit in
/usr/local/bin, they protect you even if you runsudo sn install -g.
safe-npm requires jq to quickly parse the JSON dependency tree.
# On Debian/Ubuntu
sudo apt update && sudo apt install jq -ySince this setup applies system-wide, you will need sudo privileges to copy the files into the correct directories.
1. Clone the repository and enter the directory
git clone https://github.com/medreseli/safe-npm.git
cd safe-npm2. Make all scripts executable
chmod +x sn snx npm npx sn-core.sh3. Copy the core engine to the system libraries folder
sudo cp sn-core.sh /usr/local/lib/4. Copy the executable commands to the system binaries folder
sudo cp npm npx sn snx /usr/local/bin/Installation is complete! The system will now route all users through safe-npm.
Simply replace your normal Node.js workflow commands with sn and snx.
# Instead of npm install react
sn install react
# Instead of npm run build
sn run build
# Instead of npx create-react-app my-app
snx create-react-app my-appIf you or another user accidentally types npm install, you will see:
[BLOCKED] Installation commands are disabled via raw 'npm' for security.
Would you like to run 'sn install' instead? (Y/n):
Simply press Enter or Y, and safe-npm will automatically execute the safe process for you.
safe-npm distinguishes between safe local commands and dangerous registry-facing commands.
The following will be blocked when using raw npm, prompting you to run with sn:
npm install/npm i/npm cinpm addnpm update/npm up/npm upgradenpx <remote-package>
The following bypass the safety checks and will pass through directly to native binaries:
npm run <script>npm testnpm listnpm link(inherently safe local symlinking for local development)npx <local-package>(if already installed in node_modules)
If you try to install a package that includes a recently published dependency, you will see a warning like this:
[Security Check] Simulating install to map ALL nested dependencies...
[Security Check] Checking age of dependencies in parallel...
WARNING: RECENTLY PUBLISHED DEPENDENCIES DETECTED
The following packages (including nested dependencies) are less than 7 days old:
-> express@4.18.3 (Published 2 days ago)
-> hidden-malicious-dep@1.0.1 (Published 0 days ago)
Malicious packages are often removed within 72 hours of publication.
Are you sure you want to proceed? (y/N):
- Interception:
/usr/local/bin/sncatches your command before the real Node.js binaries see it. - Dry Run Simulation: It executes
npm install <packages> --dry-run --json. This forces NPM to resolve the entire dependency tree without actually downloading or executing anything. - JSON Parsing: It passes the resulting data to
jqto extract a clean list of every single package and exact version that will be added or updated. - Local Cache Check: It checks
~/.cache/safe-npm/for the publication date of each version. If found, it skips the network request. - Concurrent API Checks with Worker Control: For uncached packages, it spins up parallel background processes (
&). It monitors active process IDs to keep execution capped at a pool of 15 concurrent workers to protect CPU limits and prevent HTTP 429 (rate-limiting) responses from the NPM registry. - Evaluation: It converts timestamps to UNIX epoch time and compares them to the configured threshold.
By default, the script flags any package newer than 7 days. To change this, edit:
sudo nano /usr/local/lib/sn-core.shChange NPM_SECURITY_DAYS=7 to your preferred number.
If you wish to clear the verification cache and force the script to re-check the registry for all packages:
rm -rf ~/.cache/safe-npmsafe-npm is fully compatible with NVM. However, because NVM places its binaries at the front of your $PATH, you must create a global shell function routing:
- Open the global bashrc:
sudo nano /etc/bash.bashrc - Paste at the bottom:
npm() { /usr/local/bin/npm "$@"; } npx() { /usr/local/bin/npx "$@"; }
- Restart your terminal.
MIT License.