· Zerdalu
Go Back

Why I don't trust `npm install` (and how I fixed it on Debian)

NPM is the default for a reason, but it’s also a massive, open target for supply chain attacks. We’ve all seen the headlines: malicious packages disguised as popular libraries via typosquatting (like reacr instead of react) or hijacked maintainer accounts.

The window for these attacks is usually tiny. Security teams typically scrub malicious code from the registry within 72 hours. But that’s cold comfort if you happen to run an install command during that three-day gap. Your system is compromised before the registry can even pull the plug.

I built safe-npm to close that gap. It’s a system-wide wrapper I developed on Debian 13 to act as a proactive safety net for local development.

Why standard security checks miss the mark

Most security scripts only scan the package you actually typed into the terminal. The real danger is usually buried five levels deep in the dependency tree. If a popular library updates its sub-dependencies and one of those has been hijacked, a standard install will pull that malware onto your machine without a single warning.

How safe-npm intercepts the threat

The project works by disabling the native npm and npx commands. They are replaced by two safer alternatives: sn (Safe NPM) and snx (Safe NPX).

Here’s how the logic actually flows:

  1. The Interception: When you run sn install <package>, the script halts the process before a single byte of code is downloaded.
  2. Dry Run Mapping: It performs a “dry run” to generate a JSON map of the entire dependency tree—every nested package and exact version that would be installed.
  3. The 7-Day Rule: Using the NPM registry API, it checks the publication date for every package in that tree. If anything was published less than 7 days ago, the script kills the process and flags it.

Since most malware is caught within a few days, this 7-day buffer practically eliminates the risk of installing a “zero-day” malicious package. To keep this fast on Linux, these registry queries run concurrently in the background.

Outsmarting NVM Path Priority

The biggest headache in securing NPM on Linux is the Node Version Manager (NVM). NVM prepends its own binary path to the front of your shell’s $PATH, which effectively allows it to ignore any security scripts you put in /usr/local/bin.

To fix this globally for everyone on a Debian system, safe-npm uses a global function injection in /etc/bash.bashrc. By defining npm() and npx() as functions, the shell prioritizes our security wrapper over any binaries it finds in the $PATH. Even if a user tries to use the raw commands, the system forces them through the safe alternatives.

The Stack

The logic is split into three parts:

  • The Core Engine (sn-core.sh): This is the “brain” that parses dependency trees via jq and handles registry queries.
  • Command Wrappers (sn/snx): The new primary commands for your daily workflow.
  • The Blockers (npm/npx): Stub scripts and shell functions that keep you from accidentally using unprotected commands.

By housing these in /usr/local/bin and /usr/local/lib, the policy applies to every user on the machine, including root and service accounts.

Security is about layers. This doesn’t replace an official security audit, but it adds a necessary bit of friction. It forces you to stop and think when a package is suspiciously new, potentially saving your machine from a compromised dependency before it ever touches your disk.

You can check out the source code and installation guide here: https://github.com/medreseli/safe-npm