GitHub's Package Firewall: Stopping Malware Before It Runs

·11 min read·Evergreen Tools Team

On October 20, 2026, GitHub put a package firewall into public preview. The problem it addresses is narrow and nasty: a growing share of malware attacks complete their work at package installation time, harvesting credentials on CI/CD hosts or developer workstations without ever running the package's code. That defeats the usual instinct of reading source before trusting a dependency, because you never get far enough to run anything. GitHub's answer is to insert a checkpoint between endpoints such as Actions runners and coding agents and the upstream registries, so both metadata requests and package downloads pass through it and get evaluated against enterprise policy. Three things are worth understanding: what the policy controls actually are, how to find the endpoints still bypassing it, and why the cooldown period is the highest-value setting on the list.

Server racks and network cables

The firewall sits between your runner and the registry, not inside your code

1. It Does Not Defend Against Bad Code That Runs

Get the threat model right or you will use the wrong tool. GitHub's framing is that 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. What can installation do? It can execute install scripts, read tokens from environment variables, touch mounted credential files, and make outbound network calls. So the attack surface opens the moment your pipeline runs an install, regardless of whether the package's functionality is malicious. The right interception point is therefore before the download, not after execution. GitHub positions the firewall between endpoints, meaning Actions runners and coding agents, and the upstream package repositories, inspecting metadata requests and downloads alike.

# 1) Find every endpoint still talking straight to the public registry.
#    A firewall only covers the paths you actually moved behind it.

grep -rn --include=".npmrc" --include="*.yml" --include="*.yaml" \
     --include="Dockerfile*" --include="*.sh" \
     -e "registry.npmjs.org" -e "registry.yarnpkg.com" \
     . 2>/dev/null | grep -v node_modules

# Common survivors: Dockerfiles, one-off bootstrap scripts, legacy repos,
# and the developer laptop that never got the new .npmrc.

2. Policy Controls: Three Knobs You Can Turn Today

Administrators set policy at the enterprise level, and the controls available today are a configurable cooldown period, checks against the GitHub Advisory Database for malware or vulnerability alerts, and supply-chain checks such as attestation downgrades. Endpoints then need to be configured to send their requests through the firewall rather than pointing straight at upstream registries. That step is where deployments quietly go wrong, because setting enterprise policy is not the same as routing every endpoint through it. Once traffic flows through, metadata requests and downloads are evaluated against policy, and a package blocked erroneously can be appealed through a workflow similar to other GitHub Advanced Security features. Three boundaries to remember: only npm packages are supported today, it is available to all GitHub Enterprise Cloud customers including data-resident deployments, and it is free during the preview with metered usage billing expected at general availability, similar to Actions.

# 2) Point installs at the firewall registry GitHub provisions for you.
#    Use the exact URL from the setup docs -- do not hand-write one.

# ~/.npmrc for CI, and the same file committed for repo-level config
registry=${GITHUB_PKG_FIREWALL_REGISTRY}
always-auth=true

# Prove the metadata request is routed through the firewall, not around it
npm config get registry
npm view lodash version --loglevel=http 2>&1 | grep -i "http fetch" | head -3

3. Why the Cooldown Is Worth More Than It Looks

Of the three knobs, the cooldown is the most underrated, because it sounds the most primitive: just wait a few days before installing a new version. But it targets the most common shape of attack right now, a hijacked maintainer account shipping a backdoored patch release within hours. Those releases tend to have short lifespans, because the community, security researchers and automated monitoring find and pull them within days. A cooldown puts you in the queue behind people who step on the mine first. The Python snippet in position three does something simple: check how long ago the version you are about to install was published. If the answer is six hours, a seven-day cooldown makes it wait. The real value is not the script, it is turning a vague policy into a checkable fact, so you can answer which versions the cooldown blocked and whether anyone routed around it.

# 3) Cooldown, in numbers: how old is the version you are about to install?
#    A cooldown is not paranoia. It is the window in which the ecosystem
#    notices a hijacked maintainer account before your CI installs it.

python3 - <<'PY'
import json, urllib.request, datetime as dt

name = "example-package"
meta = json.load(urllib.request.urlopen(f"https://registry.npmjs.org/{name}"))
latest = meta["dist-tags"]["latest"]
published = meta["time"][latest]
when = dt.datetime.fromisoformat(published.replace("Z", "+00:00"))
age_h = (dt.datetime.now(dt.timezone.utc) - when).total_seconds() / 3600
print(f"{name}@{latest} published {published}  age={age_h:.1f}h")
print("within 7-day cooldown" if age_h < 168 else "passes cooldown")
PY

4. Find the Endpoints Still Bypassing It

A firewall only protects the paths you moved behind it. The grep in position one is the most useful command here: search the repository for anything still pointing directly at registry.npmjs.org or registry.yarnpkg.com. The usual survivors are Dockerfiles running npm install, one-off bootstrap scripts, legacy repositories nobody maintains, and the developer laptop whose .npmrc never got the new setting. That last one is the most dangerous, because it sits outside every pipeline while holding the richest credentials. Once you have the list, point installs at the registry GitHub provisions for your enterprise, using the exact URL from the setup documentation rather than one you assemble by hand. Then prove it with npm config get registry and a query run with loglevel=http, so you can see the fetch going through the firewall instead of assuming your config edit took effect.

# 4) Do not let a lockfile change slip through review unread.
#    Malware at install time often arrives as a patch-level bump.

git fetch origin main --depth=1

diff <(git show origin/main:package-lock.json) package-lock.json \
  | grep -E '^[+-].*"(resolved|integrity|version)"' \
  | head -40

# Flag anything that changes host, drops integrity, or resolves to a
# tarball URL outside your approved registry.

5. Turn "No Bypass" Into a Build Gate

Policy, cooldown and advisory checks are all enterprise-level settings, but the last mile decides whether any of it works: does your pipeline assert that it is going through the firewall? The snippet in position five gives two CI assertions. One fails the build when npm's effective registry is not the firewall address. The other fails when the lockfile has no integrity hashes, because without them you cannot even prove the tarball you installed is the one that was resolved. The point is to convert a security setting from documentation into a compile-time constraint. Position four adds the habit that gets skipped: read the lockfile diff in a pull request. Install-time malware frequently arrives disguised as a patch-level bump, so a diff that changes the host, drops integrity, or resolves to a tarball outside your approved registry deserves a stop-and-look.

# 5) Make bypass a build failure, not a code review comment.
- name: Assert installs go through the package firewall
  run: |
    reg=$(npm config get registry)
    echo "registry=$reg"
    case "$reg" in
      *"$GITHUB_PKG_FIREWALL_HOST"*) echo "ok: firewall in path" ;;
      *) echo "FAIL: installs bypass the package firewall"; exit 1 ;;
    esac

- name: Assert lockfile integrity is present
  run: |
    if grep -q '"integrity"' package-lock.json; then
      echo "ok: integrity hashes present"
    else
      echo "FAIL: lockfile without integrity hashes"; exit 1
    fi

6. What to Do Now

Four steps, in order. First, if you are on GitHub Enterprise Cloud, enable the preview and walk the documentation; it costs nothing during preview. Second, run the grep and list every endpoint and config file still pointing at public registries. That list is itself a security status report. Third, set a cooldown to a value your team can tolerate, seven days for example, and record which versions it blocked so you gain visibility rather than just latency. Fourth, add the registry and integrity assertions to CI so bypass becomes a build failure instead of a review comment. One boundary worth repeating: this covers npm only. Your Python, Go and Ruby dependencies are outside the perimeter, so do not let npm coverage read as whole-supply-chain coverage. For context, GitHub also updated secret scanning and added npm 12, pnpm 11 and 12, and Bun 1.4 support for Dependabot the same day. Read together, those three moves tell the whole story: notice the leak, update the dependency, and stop the install.

Build pipeline concept art

A cooldown buys time for the ecosystem to notice a bad release

📌 Frequently Asked Questions

What kind of attack does the package firewall stop?

Primarily install-time supply chain attacks. Per GitHub, 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, which is why interception has to happen before the download rather than at runtime.

Which package managers are supported?

Only npm packages today. Dependencies from the Python, Go, Ruby and other ecosystems are outside its coverage, which is easy to misread when assessing overall supply chain posture, so do not treat npm coverage as whole-chain protection.

What policy controls are available?

GitHub currently lists a configurable cooldown period, checks against the GitHub Advisory Database for malware or vulnerability alerts, and supply-chain checks such as attestation downgrades. Endpoints must also be configured to route through the firewall before metadata and downloads can be evaluated against policy.

How much does it cost?

It is free during the public preview and available to all GitHub Enterprise Cloud customers, including GitHub Enterprise Cloud with data residency. GitHub says it will be charged based on metered usage at general availability, similar to Actions billing.

What happens if a package is blocked by mistake?

Users can request an exception through a workflow similar to other GitHub Advanced Security features. Keep a record of why exceptions are granted, otherwise requesting exceptions becomes routine and the cooldown quietly stops doing anything.