BytesafeDependency Firewall

Execution phases

The three things a package manager asks a registry for, the firewall phase that acts on each, and how to pick the phase when you write a rule.

A package manager asks a registry for three things: which versions of a package exist, the file for one of those versions, and to accept a file it is publishing. A firewall answers all three, so all three are points where your rules can act.

Every rule runs in exactly one of them. The three execution phases are versions, download, and upload. The dashboard capitalizes them in its Execution Phase picker; the config, API, and CLI use the lowercase spelling, which is what this page uses.

Each phase gives something up. A versions rule can change the outcome of an install, but it only knows what the registry says about a package. A download rule can open the package and look inside, but by then the package manager has committed to one version, so the only options are allow or fail. The phase decides what a match costs the developer, which is the choice worth settling before you write the rest of the rule.

A policy is not a rule. A policy is what your organization requires: no dependency younger than a week, no copyleft license in a shipped product, nothing carrying a known critical vulnerability. A rule is how one phase enforces part of that. One policy is usually several rules, sometimes across different phases, plus the exceptions you decide to grant. This page is about the phase a rule runs in; who sets the policy behind it is covered in Prevention model.

What the package manager asks for

What the package manager needsThe request it makesPhaseWhat a rule can do
To find out which versions existThe package's version listversionsLeave versions out of the answer
To get the file for one versionThe package filedownloadServe it, or fail the request
To publish a fileThe uploaduploadAccept the publish, or reject it

The firewall answers each of these in the ecosystem's own protocol, so the package manager talks to what looks like a registry and gets answers shaped by your rules (see Client operations).

Which of the three you get depends on what the project already carries, not on the command someone typed. A project that declares ranges has versions to work out; a project with a lockfile, or with exact versions written down, has none, so installing it is downloads and nothing else. The phase follows the request, not the command.

The three execution phases

Versions phase

This phase is version resolution. A project depending on lodash ^4.17.0 has not named a version: the caret means any 4.x release from 4.17.0 upward, so the package manager has to find out what exists before it can choose. That question is what this phase answers, for the whole dependency tree, and a build installing from a lockfile never asks it. The rules still apply to those installs, enforced at download time instead; the callout below explains the difference.

Package manager
Firewall
Upstream
Ask which versions exist
Fetch metadata
All versions and tags
versions rules filter the list
Allowed versions only
picks the newest allowed version, repeats for every dependency
Package manager
Firewall
Upstream
request response

A blocking rule here removes the matching versions from the metadata response before the package manager sees it, along with any tags pointing at them. The package manager then picks the newest version that is left and still satisfies the dependency range, so resolution lands on something that complies with your rules.

Upstream answer
4.17.232 days old4.17.224 days old4.17.213 months old4.17.201 year oldevery published version
versions rules
Firewall rewrites the response

block versions younger than 7 days

What the package manager sees
4.17.213 months old4.17.201 year old^4.17.0 resolves to 4.17.21

It is also how you control which copy of a package wins when upstreams overlap, say a patched internal build alongside the public release. The package manager picks from the combined list, and a versions rule decides what goes into it.

Two limits are worth knowing:

  • There has to be another version to pick. An exact pin of a removed version, or a tag pointing at one, has nothing to fall back to and fails.
  • It only sees what the registry knows. A versions rule can use the package name, the upstream, registry status such as deprecation, and the publish date (maxAgeHours). Anything that needs the package file itself is out of reach.

Do exact pins or lockfiles bypass the filter?

No, but they fail differently. An exact pin (react@18.2.0) is checked against the filtered list, so the package manager reports that version as not found. A request that skips the list entirely and goes straight for the package file, such as a tarball URL taken from a lockfile, is checked against the versions rules at download time instead. That one fails with the same explicit error a download block gives: the package, the version, and a request ID, recorded in the firewall log as a download entry.

Either way, a version a versions rule removed cannot get in. What differs is the error the developer sees. "This version does not exist" when the package manager used the version list, an explicit block when it went straight for the file. Rules that should always fail loudly still belong in download, alone or in addition.

Download phase

This phase is the download itself. Every package that reaches a build is fetched one file at a time, whether its version was resolved a moment earlier or written into a lockfile months ago. The firewall has that file in hand when it decides.

Package manager
Firewall
Upstream
Ask for one version's package file
Fetch artifact
Artifact
versions rules, then download rules, run on that version
Artifact served, or an error with package, version, and request ID
Package manager
Firewall
Upstream
request response

A blocking rule here fails the request. The package manager has already committed to a version, so there is nothing for it to fall back to. download rules run once per package fetched, with the package file available to read, which is what makes content checks possible.

What this phase gives you is coverage that resolution cannot provide. Every package file entering a build passes through it, including the dependencies several levels down that no developer picked. Nothing gets in by being requested a different way, which is what makes a download rule the one you can state as a guarantee, to an auditor or to a customer.

Use it when a different version of the same package would not make the problem acceptable: a malware verdict, a vulnerability threshold, a package that must never be fetched from outside your organization.

A block answers the request with the firewall's error instead of the package file. How much of that error a developer sees depends on the package manager, so each ecosystem page shows what its clients print. The reason lives in the firewall log either way: the block carries a request ID, and the entry it points at names the rule that fired, the package, and the version. See Investigate a blocked install.

Upload phase

This phase is publishing. When a developer or a pipeline pushes a package to your publish target, it goes through the firewall on the way. It is the only phase that looks at packages leaving your organization.

Package manager
Firewall
Publish target
Publish this package file
upload rules run on the file
Forward the publish, if no rule blocked it
Accepted
Published, or rejected with package, version, and request ID
Package manager
Firewall
Publish target
request response

upload rules evaluate publishes before they reach the publish target. A blocked publish is never forwarded, and the rejection names the package, the version, and a request ID. Because this is where you can still stop something before it leaves, secrets scanning runs here. Malware and install-script checks run here as well, so what you publish is held to the standard you apply to what you consume.

Comparing versions and download

The two phases that act on packages coming in are the pair you choose between when you write a rule.

versionsdownload
Runs whenThe package manager asks which versions existThe package manager fetches one package file
Can useName, version, upstream, publish date, registry status such as deprecationAll of that, and the package contents
On a matchThe version is left out of the answerThe request fails
The developer seesA successful install, possibly on an older versionA failed install, with a block from the firewall behind it

Which checks run in which phase

The phase also decides which selector functions a rule can use, and that often decides the phase for you. Check the selector function matrix before writing a rule. A function used outside its phases never matches, so the rule saves cleanly and then does nothing.

Two consequences come up often. Vulnerability and license checks can only run in download, so rules built on them always fail the install rather than steering it. Package age, deprecation, unlisted status, and publishing trust are registry facts, so rules using them can run in versions and steer resolution instead.

What each phase records

  • A versions rule with log: true records the versions it removed, so filtering stays visible in the firewall log even though the install succeeded.
  • Webhook events come from download and upload decisions. Metadata filtering never produces events, however many versions it removes. A versions rule that blocks an artifact request is a download decision, so that block does produce one.

Why the phase you pick matters

A policy that developers cannot work with does not survive. It gets waived case by case, then switched off. Having more than one enforcement point means most of what you want to enforce can apply to every install without that outcome, while the rules that should stop a build still do.

Filtering versions: enforcement that keeps builds running

Take delaying new versions, where a release has to be a week or two old before builds may use it, because a malicious release is usually reported and pulled within days of publication.

As a download block, that policy is unworkable. It would fail any install that resolved to a release published last week, including dependencies of dependencies nobody chose deliberately, and keep failing until that release aged out. Pipelines break, developers are blocked on something they cannot fix, and exception requests queue up until somebody takes the rule off.

A versions rule enforces the same policy while the package manager is still deciding, by rewriting the version list so the newest release satisfying both the dependency range and your policy is the one that gets picked. The build runs, nobody waits for an approval, and no ticket is filed, while log: true keeps a record of every version the rule removed.

Deprecated and unlisted versions, publishing-trust downgrades, and blocks on a particular name or upstream all steer resolution the same way.

Failing the download: enforcement with no acceptable alternative

Some requirements have no acceptable alternative version. Malware is the clear case: no other release of a package that ships a payload is the one you wanted, and quietly resolving around it would hide that someone tried to install it. Here a failed build is the point. The install stops with an error from the firewall rather than a missing version, and the log entry behind it names the rule, the package, and the version, enough for whoever owns policy to decide between a fix and a scoped exception.

Choosing a phase for a rule

Start from what you want to enforce rather than from the phase. Two questions settle it.

  1. Would another version of this package be acceptable? If yes, versions enforces it and lets the install continue on a version that complies. If no version of it may enter, download is the phase that says so.
  2. Does the check need to read the package file? If it does, the phase is decided for you: download for what comes in, upload for what you publish. The selector function matrix is the list.

Many requirements want both phases, and the two rule sets are separate gates, so a version that survives filtering is still evaluated on download. A common baseline is a maxAgeHours window in versions to delay fresh releases without breaking installs, plus malware and vulnerabilities rules in download as hard stops. Pairing the same selector across both phases works too, when you want resolution steered away from something and a hard stop if a lockfile asks for it anyway.

What you want to enforcePhaseWhat the developer sees
An acceptable version exists, so send installs to it instead (age windows, deprecated packages)versionsThe install succeeds on an older version. Nothing fails
Installs must only ever choose from versions you control (overlapping upstreams, internal builds)versionsThe package manager only ever sees the versions you allow
No matching version may enter, whatever that breaks (malware, vulnerabilities)downloadThe install fails, including when the package is a dependency of a dependency
Control what gets published (secrets)uploadThe publish is rejected before it reaches the upstream

Starting points in the dashboard

You do not have to pick a phase cold. Add Rule opens with a short list of common goals, each one a policy people usually want, and choosing one starts the rule in the phase that policy needs.

The goal you pickWhere it starts the rule
Let new releases matureversions, with a 7-day window already filled in
Hide packages or versionsversions
Block packages from being downloadeddownload
Check packages you publishupload, offered for ecosystems that have a publish path
Start from a blank ruleNothing preselected

What you get is a starting point, not a finished rule. The next screen is the normal rule form, where you add the selector that says which packages to match and the selector function that says what to check, and where every seeded value stays editable, phase included.

The how-to guides each work one policy through end to end and state the phase they use and why.

On this page