DEV Community

Cover image for GitHub's package firewall went into preview: npm gets checked before your agent installs it
Piekwerk
Piekwerk

Posted on

GitHub's package firewall went into preview: npm gets checked before your agent installs it

On October 20 GitHub put a package firewall into public preview. It sits between "endpoints like Actions runners or coding agents" and the upstream package registry, and it inspects both the metadata requests and the package downloads that flow through it (changelog). The framing is right: the thing that needs a firewall is not your browser, it is the process that runs npm install on your behalf.

Why install time is the attack surface

The changelog opens with a sentence worth quoting in full: "An increasing number of malware attacks exploit systems at package installation time, harvesting credentials on CI/CD hosts or developer workstations without even running the package's code."

Install is execution. npm lifecycle scripts run inside a shell that carries your environment, your GITHUB_TOKEN, your cloud keys, sometimes your publish token. A hostile package never needs your app to import it. Coding agents widen the hole because they decide to add dependencies on their own, at hours nobody is reading the diff, inside an environment you configured once and forgot about.

What the firewall actually checks

There are three policy controls today, all configured at the enterprise level:

  1. Cooldown period. Blocks package versions newer than a minimum age you set. The docs' phrasing is the best argument for it: "allowing time for threats to be discovered before you install them." Most malicious packages get reported and pulled within hours, not days.
  2. Advisory lookups. Every request is checked against the GitHub Advisory Database for malware and vulnerability alerts.
  3. Supply-chain checks, "like attestation downgrades", where a signed, attested build gets swapped for an unsigned one somewhere between the publisher and you.

Both halves of the traffic get inspected: the metadata your package manager resolves against, and the tarballs it fetches.

The setup part that is easy to miss

The firewall is not a switch you flip in settings. From the docs: endpoints "need configuration to use the firewall for their requests instead of pointing directly at the upstream package registries." For npm that means a user-level .npmrc:

registry=https://package-firewall.githubapp.com/npm/enterprises/ENTERPRISE
Enter fullscreen mode Exit fullscreen mode

Each request carries a token from a user or bot account inside the enterprise. One detail I appreciate: the docs say the token needs nothing beyond read-only access to public repositories. No elevated scopes to hand out, no reason to reuse a powerful token.

If you run Artifactory or another proxy in front of npm, the docs have a path for that too, and one instruction matters more than the rest: exclude any remote that still fetches public npm directly, "so they cannot bypass the firewall." Coverage is a routing property. A laptop, a container image, or an agent sandbox still pointed at registry.npmjs.org keeps its old exposure, and nothing warns you.

What a block looks like in practice

When a version fails policy, npm prints a notice with the reason and a link:

npm notice testcontainers: 1 version blocked by package-firewall due to
security policies (version cooldown). Request an override: https://github.com/enterprises/..
Enter fullscreen mode Exit fullscreen mode

That override link is a real workflow, which means someone has to triage it. Decide before you roll this out what an unattended coding agent should do at 2am when a dependency it wants is on cooldown: stop and report, or wait for you. Auto-approving overrides would quietly delete the whole control.

Who gets it, and what it costs

  • Public preview, available to all GitHub Enterprise Cloud customers, including the data-resident version. Not Team, not Free.
  • npm only, for now. Python, Go, and Maven ecosystems are not covered yet.
  • Free during the preview. At general availability it becomes metered usage billed like Actions, so check what your CI traffic costs before flipping it on org-wide later.

The other trust boundary that moved this week

The same 48 hours shipped Claude Code 2.1.305 and 2.1.306, and two lines in that changelog matter to anyone running allowlists:

  • allowedMcpServers no longer filters managed-mcp.json stdio servers that use ${VAR} only in env. The changelog states the consequence plainly: "one your allowlist left out now loads on upgrade; use deniedMcpServers to keep it off." An allowlist you configured as fail-closed just gained a fail-open exception, on upgrade day.
  • sandbox.allowNestedUserNamespaces lets sandboxed Linux commands create user namespaces (Chromium, rootless podman), and the changelog itself notes this "lets them get around the sandbox's file protections."

Two vendors moved the boundary that decides what loads inside an agent session, in the same two days. After an upgrade, diff what actually loads instead of assuming your last audit still holds. I wrote about guards that silently stopped doing their job before, and the pattern has not gone away (Claude Code 2.1.294 fixed a batch of those).

Four rules that hold on every plan

You do not need an enterprise plan to run the same policy yourself. This is the package-install section I keep in agent rules files:

## Package installs
- Do not install a package version published less than 72 hours ago. If only a
  newer version exists, stop and report it instead of forcing the install.
- Never run npm install in an environment that holds production secrets, deploy
  keys, or a token with publish scope. Installs get a scoped, read-only token.
- In CI and agent tasks, install from the committed lockfile with npm ci, never
  npm install.
- Before adding a dependency, read its lifecycle scripts first:
  npm view <package> scripts. If there is a postinstall, surface it and wait.
Enter fullscreen mode Exit fullscreen mode

The first rule is the cooldown control, run by hand. The second is the one that saves you when every screen fails, because the credential-harvesting installs described in GitHub's own announcement cannot take what was never in the room. Config files that leak secrets into agent environments are their own problem, one I scanned for here: your agent's config files probably have secrets in the clear.

What I would do this week

  1. On Enterprise Cloud: enable it on one org, point one CI runner and one agent host at the firewall, pick a cooldown, and watch the override queue for a week before widening it.
  2. On every other plan: adopt the four rules above, commit lockfiles, and scope the token your agent installs with. Hooks can fail closed too, if you configure them to (2.1.295 added exactly that).
  3. After your next Claude Code upgrade, compare the servers in managed-mcp.json against deniedMcpServers before the first session starts.

The firewall is a good sign. The industry is starting to treat agent installs as a network boundary. But today it is enterprise-only and npm-only, and the boundary you actually control this afternoon is still the rules file and the token scope.

Top comments (1)

Collapse
 
abhinavs1920 profile image
Abhinav Sharma •

The cooldown period is the quietly strongest control of the three. Most malicious packages get pulled within hours, so refusing versions younger than a set age removes the whole race. Install-is-execution is the right framing, lifecycle scripts run with full credentials before anyone reads a diff.