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 needs | The request it makes | Phase | What a rule can do |
|---|---|---|---|
| To find out which versions exist | The package's version list | versions | Leave versions out of the answer |
| To get the file for one version | The package file | download | Serve it, or fail the request |
| To publish a file | The upload | upload | Accept 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.
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.
block versions younger than 7 days
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
versionsrule 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.
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.
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.
versions | download | |
|---|---|---|
| Runs when | The package manager asks which versions exist | The package manager fetches one package file |
| Can use | Name, version, upstream, publish date, registry status such as deprecation | All of that, and the package contents |
| On a match | The version is left out of the answer | The request fails |
| The developer sees | A successful install, possibly on an older version | A 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
versionsrule withlog: truerecords the versions it removed, so filtering stays visible in the firewall log even though the install succeeded. - Webhook events come from
downloadanduploaddecisions. Metadata filtering never produces events, however many versions it removes. Aversionsrule that blocks an artifact request is adownloaddecision, 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.
- Would another version of this package be acceptable? If yes,
versionsenforces it and lets the install continue on a version that complies. If no version of it may enter,downloadis the phase that says so. - Does the check need to read the package file? If it does, the phase is decided for you:
downloadfor what comes in,uploadfor 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 enforce | Phase | What the developer sees |
|---|---|---|
| An acceptable version exists, so send installs to it instead (age windows, deprecated packages) | versions | The install succeeds on an older version. Nothing fails |
| Installs must only ever choose from versions you control (overlapping upstreams, internal builds) | versions | The package manager only ever sees the versions you allow |
| No matching version may enter, whatever that breaks (malware, vulnerabilities) | download | The install fails, including when the package is a dependency of a dependency |
| Control what gets published (secrets) | upload | The 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 pick | Where it starts the rule |
|---|---|
| Let new releases mature | versions, with a 7-day window already filled in |
| Hide packages or versions | versions |
| Block packages from being downloaded | download |
| Check packages you publish | upload, offered for ecosystems that have a publish path |
| Start from a blank rule | Nothing 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.
Related
- Concept: Rule evaluation, Exception model
- How-to: Delay new versions (
versions), Block malware (download), Scan for secrets on publish (upload), Investigate a blocked install - Reference: Client operations, Rules and selectors, Selector functions