The install step is the most dangerous moment in your day

It is a Tuesday morning. A developer pulls a popular open-source package, one they have installed a hundred times, and runs the install. Nothing looks wrong: the progress bar fills, the prompt returns, they move on. What they cannot see is that the version they just pulled went up four hours ago, and that buried in its install script are a few lines that read their SSH keys and environment variables and post them to a server they have never heard of. By the time an advisory is filed, the keys are already gone.

This is not hypothetical. It is how the TrapDoor campaign worked in May 2026. More than 34 malicious packages went up across PyPI, npm and Crates.io at roughly the same time, each built to run hostile code the moment it was installed or imported.

The damage happens at install, not at runtime

Most people picture a vulnerability as something that bites later, when the app runs in production. Supply-chain attacks do not wait that long. A post-install script, or a package that executes on import, runs on the machine doing the installing: a laptop, a CI runner, a build server, with that user’s permissions. That is precisely where SSH keys, cloud tokens and .env files live. The install step is the moment the attacker’s code and your secrets are in the same room.

Why “after” is too late

Vulnerability dashboards and audit reports are useful, but they look backwards. They tell you what is already installed. An attacker who slips a malicious version into a popular package is counting on that gap, the hours or days between publish and detection, to reach as many machines as possible. By the time a scanner flags the version, the install has already run.

Freshly published releases are the sharp edge of this. A brand-new version has had the least scrutiny, which makes it both the most likely to be hostile and the least likely to be flagged yet. The most dangerous package on any given day is usually the newest one.

It is worse on a build server

A laptop is one machine. A CI runner is a machine that touches everything. Continuous-integration pipelines install dependencies fresh on every run, usually with broad credentials sitting right there in the environment: deploy keys, registry tokens, cloud roles. A malicious post-install script on a CI runner does not just compromise one developer. It can reach the artifacts you ship to everyone downstream.

And because CI installs from scratch every time, a poisoned version is pulled the moment it is published, automatically, with no human in the loop to notice that anything looked off. The install step is dangerous on a laptop and outright critical in a pipeline. That is exactly where a check that runs before install, the same way every time, earns its place.

Move the check earlier

A pre-install gate changes the order of operations. Before anything is written or executed, it:

  1. Resolves what the package manager is about to install: every package and its exact version, transitive dependencies included.
  2. Checks each one against the public vulnerability databases: NIST NVD, OSV.dev and the GitHub Advisory database.
  3. Applies a freshness hold. Any release younger than a few days is treated as unproven and held back, because that is the window supply-chain attacks live in.
  4. Fails closed. If anything is known-bad, suspiciously new, or simply cannot be verified, the install stops. Nothing is written, nothing is executed.

The freshness hold, plainly

Why hold new releases at all? Because the evidence is lopsided. A version public for weeks has been downloaded, imported and read by thousands of people. If it were hostile, the odds are someone would have noticed. A version that went up this morning has had none of that. The freshness hold simply says: let the crowd test it for a few days first. You lose almost nothing, since the version you actually want is usually already days or weeks old, and you side-step the exact window attackers depend on.

“Won’t this get in my way?”

A fair worry: a gate that nags is a gate people switch off. So these are built to be quiet. They hold only the newest releases, not your whole dependency tree. Results are cached, so repeat installs are not re-scanned from scratch. And when a registry or database is unreachable, the gate fails closed rather than waving things through. You do not notice it until the day it stops something, the only day it matters.

“I pin my versions. Isn’t that enough?”

Pinning and lockfiles are good hygiene, but they solve a different problem. A lockfile guarantees that everyone on a project installs the same versions. It protects you from drift, not from a bad version you pin in the first place. The moment you add a dependency or bump an existing one, you resolve a fresh version and write it into the lockfile yourself. If that version is the poisoned one, the lockfile faithfully pins the attack and ships it to the whole team.

A pre-install gate sits one step earlier. It checks the version at the moment you would add it, before it ever reaches the lockfile. The two work together: the gate decides whether a version is safe to pin, and the lockfile keeps everyone on the version the gate already approved.

Turning it on

The point of a gate is that it is on by default, so switching it on should be a one-time job, not a habit. Each 5bats gate installs as a thin wrapper in front of the package manager you already use. You install it once, and from then on every install is resolved and checked before it runs, with no flag to remember and no separate command. From there the gate is invisible until the day it stops something. There is nothing to wire into each project, no dashboard to watch, and no per-install decision to make. That is the whole point: the safest behaviour is the one that happens whether or not anyone remembers to ask for it.

Fail closed, stay boring

Security tooling earns trust by being predictable. A 5bats gate that cannot verify something denies it, every time, with no surprises and nothing to configure. Boring, default-on protection beats clever protection that waits to be set up.

The same gate exists for the package managers most likely to be in your day: pip , Composer and Homebrew . Each makes the safest moment, before install, the default, with no extra step to remember.

← Back to all posts