Policy reference
The settings that turn findings into ALLOW, WARN or BLOCK - release cooldown, new-package age, licences, exceptions and baselines - with their defaults.
A policy decides what each finding does to the verdict. The account has one, under Settings → Policy in the dashboard, and a project can have its own. The same inputs always give the same verdict, and every verdict records which policy produced it, so a past decision can be explained after the policy has changed.
The defaults block on the unambiguous and warn on the suggestive. A tool that blocks on suspicion gets switched off, and a tool that has been switched off protects nobody.
Defaults at a glance
| Condition | Default |
|---|---|
| Known malware | BLOCK |
| Critical vulnerability | BLOCK |
| High or medium vulnerability | WARN |
| Critical suspicious behaviour | BLOCK |
| High or medium suspicious behaviour | WARN |
| Release published less than 7 days ago | BLOCK |
| Package that first existed less than 30 days ago | WARN |
| Release judged suspect when it was published | BLOCK |
| Invalid registry signature, or provenance that does not match | BLOCK |
| Install scripts on a package being added or changed | WARN |
| Same version republished with different contents | WARN |
| Downgrade, lookalike name, new publisher, lost provenance, deprecated version | WARN |
| Dependency PkgFire cannot fetch or verify | WARN |
| A scanner that could not run | WARN |
| None of the above | ALLOW |
Release cooldown
The cheapest strong defence there is. Most real supply-chain attacks share a shape: a hijacked maintainer account publishes a poisoned version, and the registry pulls it within hours. Refusing versions younger than a few days stops those attacks without having to recognise them.
- Block releases newer than, default 7 days, and Warn on releases newer than, default 7 days. 0 turns either off.
- End the block for reviewed releases after, default 2 days. PkgFire compares each new release with the one before it when it is published. If nothing changed in how it installs, what it depends on, its provenance or its publisher, and every check ran, the block ends after this many days. The warning stays for the full cooldown.
- A release whose publish time cannot be found warns while the cooldown is on. An unknown age is not taken as an old one.
- The fix for a known advisory is held to a warning. An upgrade to exactly the fixed version of an advisory found on the version it replaces is not blocked by the cooldown, and the reason says which advisory. Only that exact version qualifies, and only the cooldown is lowered: malware or a blocking vulnerability in the fix still blocks.
- A scheduled re-scan checks what is already installed, so there the cooldown warns at most.
New packages and publishers
- Warn on packages newer than, default 30 days, counted from the package's first ever release. Block packages newer than is off by default. A dependency that did not exist last month is the shape a typosquat or a throwaway malicious package takes.
- Warn on a new publisher, on by default: a version published by an account that never published this package before, or a Python release uploaded from a different repository than the release before it. It only warns, because projects hand releases over legitimately. Publisher check needs at least, default 5 versions, skips packages without enough history to compare.
- Warn on lookalike names, on by default: the name is one slip away from a far more popular package. It never blocks.
- Suspect releases (Off, Warn or Block, default Block): PkgFire's review of a release at publication found the shape of a hijack, such as an install script appearing, a days-old dependency being added, or provenance and publisher changing together. Block holds it until it has been checked.
Vulnerabilities and suspicious behaviour
- Block on vulnerabilities (default Critical) and Warn on vulnerabilities (default Medium) set the severity at which an advisory counts.
- Only block when reachable, off by default: where reachability is analysed, which is Go, a vulnerability blocks only if your code calls the affected function. Findings that were not analysed still count.
- Block on suspicious behaviour (default Critical) and Warn on suspicious behaviour (default Medium) do the same for code that looks malicious.
- Trust established versions after, default 30 days. Code-pattern findings on a version that has been public this long are shown but not counted, because legitimate libraries read environment variables and talk to the network too. Malware advisories and vulnerabilities are never set aside this way.
Install scripts and republished artifacts
- Install scripts (Off, Warn or Block, default Warn). Only packages this install adds, upgrades, downgrades or republishes count: a package that was already installed has run its script already. Install scripts stay off during PkgFire's own install whatever this says, unless you pass
--pkgfire-allow-scriptsand the verdict is ALLOW. - Republished artifact (default Warn): the same version now has different contents than when it was first seen.
When a scanner cannot run
Scanner failure (Off, Warn or Block, default Warn). A scanner that failed has checked nothing, and this decides whether that silence is acceptable. A laptop can fail open; a CI pipeline should fail closed, so set it to Block there.
Licences
Licences are read for npm, Python, Go and Cargo packages as SPDX expressions. Settings → Policy → Licences and upkeep has three lists:
- Block these licences: a package under any of them blocks.
- Review these licences: a package under any of them warns.
- Always allow these licences: these pass even when "Any other licence" is blocked or flagged.
"Any other licence" covers everything not named, so *Permissive only* is a permissive allow list plus a block on any other licence. An expression is judged term by term: every AND term applies, and of an OR the most permissive choice counts. So Apache-2.0 AND LGPL-3.0-or-later trips a block on LGPL, while GPL-3.0-or-later OR MIT does not trip a block on GPL.
Warn when no licence is declared is off by default, because many small packages omit the field. A package whose licence is not known at all is never judged. Each project's Licences tab lists every licence in its latest scan and what the policy makes of it.
Exceptions
Three lists, one entry per line, written ecosystem:name or ecosystem:name@version:
| List | Example | Effect |
|---|---|---|
| Always allow | npm:left-pad, npm:@scope/name@1.2.3 | A BLOCK on the package becomes a WARN, and the finding is still shown |
| Always block | npm:event-stream@3.3.6 | Blocks every scan the package appears in |
| Ignore advisories | CVE-2021-23337, GHSA-35jh-r3h4-6jhm, GO-2024-1234 | Shown, never counted. Its aliases are ignored with it |
Malware is the exception to Always allow. An entry naming only the package trusts the package, not every future release of it, so a malware finding still blocks. Only an entry pinning that exact version lowers it to a warning.
An entry that does not parse refuses the whole save. An exception that quietly failed to apply would be an exception that quietly did not exist.
Private packages lists your own package names, such as npm:@acme for a whole scope or go:git.acme.internal for every module under a host. They are not looked up in public services, and if one of those names is ever installed from the public registry the scan blocks: that is dependency confusion.
Baselines
An existing project usually scans as WARN or BLOCK on its first day, because its whole tree is judged against today's advisories. A baseline is the narrow way past that: one finding, on one exact version, in one project, accepted by a named person on a date.
pkgfire baseline # scan, then accept every current finding as known pkgfire baseline --list pkgfire baseline --clear
Accepted findings are still stored and shown, marked accepted; only the verdict sets them aside, and the scan's reasons say so. A newer version of the package, or a new advisory against the same one, is judged afresh. Malware is never accepted, whatever the baseline says. The project's Baseline tab lists every entry, who accepted it and when, and removes them one at a time.
A policy per project
On a project's Policy tab, *Customise for this project* opens the same form for that project alone. Saving creates the project's own copy; the account policy is untouched. Removing it makes the project inherit the account policy again.