Rule evaluation
The mental model behind firewall rules. Execution phases, selectors, effects, and where exceptions fit in.
Every request that flows through a firewall is evaluated against its rules. Understanding that path makes rule behavior predictable: which requests a rule sees, when it matches, and what happens on a match.
How one request is evaluated
Rules are evaluated cheapest first, not in configuration order. Rules of equal cost keep their configuration order. A matching rule with a log effect writes a log entry and evaluation continues. A matching rule with a block effect ends evaluation: the request is blocked. Disabled rules are skipped entirely.
Cost ordering keeps the added latency close to the cheapest policy that can decide the request: an expensive check is paid for only when every cheaper rule has already let the request through. It also decides attribution. Because a block ends evaluation, the rule recorded in the log is the cheapest rule that matched, not the first one configured.
The exact order is an implementation detail and can change, so do not build policy that depends on one rule being reached before another.
Which rules apply depends on the request. A metadata request runs the versions rules, a publish the upload rules. An artifact request runs both the versions and the download rules.
When a firewall inherits configuration, the inherited rules come first in configuration order, from the top of the parent chain down, followed by the firewall's own. That is the tie-break between rules of equal cost, so a rule of the firewall's own can still be evaluated ahead of an inherited one. It does not weaken the baseline: an inherited block still blocks the request, it may just not be the rule that logged it.
Execution phases
A rule runs in exactly one phase. The phase decides which requests the rule sees and what blocking means there. This section is the summary; Execution phases walks through a real install and when to pick which phase.
The dashboard rule form labels versions as filter versions; download and upload are unchanged. The config, API, and CLI use versions.
| Phase | When | Blocking means |
|---|---|---|
versions | Client asks for a package's version list, and again when it fetches an artifact | The version is filtered out of the metadata, along with tags pointing at it. Range requests and versionless installs resolve to an allowed version; exact pins and named tags fail as not found. A direct artifact request for the version is blocked with an error and a request ID |
download | Client fetches a package artifact | The download returns an error identifying the block and a request ID |
upload | Client publishes a package | The publish is rejected before reaching the publish target |
The phase also determines what information is available. Version filtering happens on metadata, so a rule there can use the publish date (maxAgeHours) but nothing that requires the artifact. Malware scanning and install-script inspection need the artifact, so they run on download and upload; secrets scanning reads the contents of what is being published, so it runs on upload only.
The two blocking styles are complementary. A versions rule steers every resolution in the dependency tree toward allowed versions and fails softly wherever the client has a choice; a download rule is the hard stop with an explicit error naming the block and a request ID. A versions rule does not leave a side door either: artifact requests are evaluated against it as well, so a lockfile URL for a filtered version is blocked, with the same explicit error a download rule returns. The phases differ in what they can inspect and how the failure surfaces, not in which requests they can stop.
Selectors
A selector decides which packages a rule applies to. It is a set of conditions that must all match:
- Where from:
upstreamId,internal(internal or external upstream) - What:
packageNameandversion, matched as text with wildcards, andversionRange, matched with the ecosystem's version semantics - How new:
maxAgeHoursmatches versions published more recently than the given number of hours - Computed:
function, a named check such asvulnerabilitiesormalware, optionally with options
Empty fields match everything, so an empty selector matches every package in the phase. packageName and version support a single wildcard at the start or end: @acme/* (prefix) or *-beta (suffix), or * alone for everything. versionRange is the semantic alternative to a version wildcard: it understands that 1.10.0 is newer than 1.9.0, and each ecosystem writes ranges its own way. Both fields exist and both still apply when set together. Notation per ecosystem: Version ranges.
Selector functions are where external knowledge enters: vulnerability data (vulnerabilities), malware verdicts (malware), artifact contents (install-scripts, secrets), registry state (deprecated, unlisted), license data (license), and publish-trust comparisons (trust-downgrade). Plain selector fields answer "which package is this"; functions answer "what do we know about it".
Effects
An effect has two independent switches:
block: truestops the request.log: truewrites a log entry on every match.
log without block is a dry run, and the standard rollout pattern: deploy the rule log-only, watch what it would block, then enable blocking. block without log works but leaves you blind; there is rarely a reason to turn logging off.
A dry run only sees the requests that reach it. A blocking rule that is cheaper to evaluate ends the request first, so a log-only malware or license rule under-reports on requests some cheaper rule already blocks.
Exceptions
An exception is attached to one rule and consulted only after that rule's selector matched. It has its own selector, and if that matches too, the rule is skipped for this request.
Two properties keep exceptions honest:
- Expiry. An expired exception stops matching, and the block returns. Waivers are temporary by construction, not by promise.
- Reason and notes. Every exception records why it exists, which is what makes the log trail meaningful in a review.
Because an exception belongs to a single rule, allowing a package through the vulnerability rule does not also exempt it from the malware rule.
Consequences
- Decisions are per request, never stored. When the data behind a selector function changes, a CVSS score raised or lowered, an advisory withdrawn, a package deprecated, the next request reflects it. See Freshness and score changes.
- A block silences the rules after it. Evaluation stops at the first blocking match, and the order is cost-based, so a cheap blocking rule can keep a log-only
vulnerabilities,license, ormalwarerule from logging that request. - A package can pass
versionsand still be blocked atdownload. The two rule sets are separate gates, not one decision, even though an artifact request runs both. - Selector conditions AND together. Two conditions in one rule mean both must hold. Two separate rules mean either can act. Use separate rules when you mean OR.
Related
- Reference: Rules and selectors
- How-to: Delay new versions, Block vulnerable packages, Manage exceptions