Back to Newsroom
Threat Intel

GitHub's New npm Firewall Blocks Risky Packages Before They Install

GitHub has opened a public preview of a package firewall that sits between developer machines, CI runners, AI coding agents and the npm registry. It refuses known malware, attestation downgrades, freshly added install scripts and brand-new versions before they ever land on a system.

GitHub's New npm Firewall Blocks Risky Packages Before They Install

GitHub's New npm Firewall Blocks Risky Packages Before They Install

Every npm install is a small act of trust. A build pulls hundreds of packages from a public registry, runs whatever lifecycle scripts they ship with, and nobody reads the code first. GitHub is now offering a way to put a policy checkpoint in front of that moment: a package firewall, released in public preview, that inspects npm metadata requests and downloads and refuses anything that breaks the rules an enterprise sets.

What happened

The firewall is available in public preview to GitHub Enterprise Cloud customers, including data-resident deployments. For now it only understands npm; other ecosystems are expected to follow.

Architecturally it is a gate placed between your endpoints or internal registries and the upstream public registry. Developer laptops, CI/CD hosts and autonomous coding agents point their package manager at the firewall instead of at the registry directly. Each request is checked against policy, and downloads that fail are blocked at install time rather than discovered later.

Administrators have to configure the policy controls themselves. The current set covers five checks:

  • Known malware. Packages carrying an active malware advisory in GitHub's advisory database are rejected outright.
  • Severity threshold. Versions affected by a published vulnerability at or above a chosen level are blocked. The default is "Critical only", with "High or higher", "Medium or higher" and "Any severity" available.
  • Attestation downgrades. The requested version's SLSA provenance attestation is compared with earlier releases. If a package that used to ship signed provenance suddenly stops doing so, the download is stopped. That pattern is a strong signal of a compromised publishing pipeline.
  • Newly introduced install scripts. Versions that add preinstall, install or postinstall hooks where previous versions had none are blocked. Install scripts execute arbitrary code and remain the favourite delivery mechanism for credential-stealing packages.
  • Minimum package age. A cooldown that refuses versions newer than a configured number of days. The range is 1 to 365 days, the default is one day, and setting it to zero disables the check.

Developers who hit a false positive, or who genuinely need a version that is still inside the cooldown, can file an exception request with a reason, which enterprise administrators and security managers then approve or deny.

Why it matters

Install time is where modern supply-chain attacks actually succeed. The typical pattern is a maintainer account takeover followed by a poisoned release with a postinstall script that harvests tokens, cloud keys and .npmrc files within minutes of publication. By the time the registry removes the version, thousands of builds have already run it.

Two trends make the window worse. CI runners install dependencies on every pipeline run with no human in the loop, and AI coding agents now add and upgrade packages on their own to complete a task. Neither reads a lockfile diff before pulling a version that is a few hours old.

That is why waiting periods are spreading across ecosystems. Over the past year npm, PyPI, Visual Studio Code extensions, RubyGems, Bun, pnpm and Yarn have all introduced some form of minimum release age. Rust is joining them: version 1.100, scheduled for 12 November 2026, adds a configurable minimum publish age in the Cargo configuration file, and when nothing is set a seven-day wait applies to crate releases by default.

It is worth being honest about the limits. A cooldown delays legitimate security fixes just as much as malicious ones unless someone grants an exception, and malicious code does not become harmless with age. A release that evades scanners simply outlasts the waiting period. What the delay earns is time: time for researchers, maintainers and registries to spot and pull a bad version before the next dependency update carries it further.

What to do

  1. If you run GitHub Enterprise Cloud, enable the preview in a non-critical organisation first, point CI runners and coding agents at it, and tune the severity threshold and cooldown before widening the rollout.
  2. Everyone else, set a cooldown anyway. Most major package managers now expose a minimum release age setting. One to three days is enough to catch the majority of hijacked releases without crippling velocity.
  3. Tame lifecycle scripts. Run installs with scripts disabled wherever your stack allows it, and keep an explicit allow-list of the few packages that legitimately need them.
  4. Commit and review lockfiles. Treat a lockfile change like a code change, especially when an automated agent generated it. A new transitive dependency or a version bump of a few hours deserves a second look.
  5. Shrink what a malicious script can steal. Use short-lived, read-only registry tokens and scoped cloud credentials on build hosts, and never leave long-lived secrets in environment variables an install hook can read.
  6. Watch provenance signals. A version that drops its attestation or gains an install script is a reason to pause, not a curiosity.

Dependency manifests, lockfile diffs, lifecycle scripts and the provenance of what a build pulls in are exactly the layer our AI-assisted code review examines, so a suspicious package is flagged while it is still a pull request rather than after it has run on a production runner.

SHARE