Policy reference (depaudit.toml)

depaudit audit looks for depaudit.toml next to the scanned path (or use --policy FILE). Every key is optional; without a policy file the defaults below apply. Unknown keys are rejected with the name of the offending key, so typos do not silently disable a rule.

A complete, commented policy lives in examples/monorepo/depaudit.toml.

Outcome model

Every check produces findings with a severity:

Severity Meaning Exit code effect
error a violation (license deny, banned, ...) audit exits 1
warning needs a human decision (license review) exits 1 only with --strict
info informational (e.g. outdated) never

Exit codes: 0 clean, 1 violations found, 2 usage or input error (bad lockfile, bad policy, unreadable file).

Top level

distribution = "saas"     # "saas" (default) or "distributed"
include_dev  = false      # evaluate dev-only packages too (default false)

distribution selects the copyleft rule set (see below). With include_dev = false, packages reachable only from dev roots are skipped by the license, package and age checks, and their vulnerabilities are warnings instead of errors. Hygiene checks (typosquats) always look at everything.

[licenses]

[licenses]
allowed  = ["MIT", "Apache-2.0", "BSD-3-Clause"]
denied   = ["AGPL-3.0-only"]
review   = ["MPL-2.0"]
unknown  = "review"       # allow | review | deny  - no license found / unparseable / unrecognised id
unlisted = "review"       # allow | review | deny  - known license in none of the lists

Decision order for one license id

  1. Exact string match (including any WITH exception) in denied, then allowed, then review.
  2. The same lookup using only the base id (so listing GPL-2.0-only also covers its WITH variants unless the exact variant is listed).
  3. Copyleft category rule for the current distribution (below).
  4. unlisted default.

An id that is not a recognised SPDX id (or an expression that does not parse) gets the unknown action.

SPDX expressions

A OR B: the package satisfies the policy if any alternative does, so the best (most permissive) verdict wins. A AND B: all terms apply, so the worst verdict wins. WITH binds tighter than AND, AND tighter than OR. The explanation says which branch decided: MIT OR GPL-3.0-only resolved via: MIT is in the allowed list.

[copyleft.<distribution>]

License families are classified as:

Category Licenses
strong GPL-1.0/2.0/3.0, EUPL, OSL-3.0
weak LGPL, MPL, EPL, CDDL, CPL, MS-RL, ... and GPL WITH a linking/classpath exception
network AGPL, SSPL, RPL, CPAL

Everything else is treated as permissive. Defaults:

Distribution strong weak network
saas review allow deny
distributed deny review deny

Override any cell:

[copyleft.distributed]
strong = "review"

[packages]

[packages]
allow = ["npm:internal-*", "pypi:legacy-tool"]   # exempt from the license check
deny  = ["npm:event-stream", "@evil/*"]           # always an error

[packages.licenses]                                # override missing/wrong license metadata
"pypi:some-pkg" = "MIT"

Patterns are shell-style globs matched against ecosystem:name (npm, pypi, golang, cargo) or the bare name.

[[banned_versions]]

[[banned_versions]]
package  = "npm:lodash"
versions = "<4.17.21"
reason   = "prototype pollution"

versions uses the ecosystem's own range syntax (PEP 440 specifiers for PyPI, npm ranges for npm and Go, Cargo requirements for crates). Pre-releases are included in the match, so <2.0.0 bans 2.0.0-rc.1 as well. See version-semantics.md.

[age]

[age]
max_days = 730
level    = "warn"    # off | warn | error

A package is stale when its latest release is older than max_days relative to --as-of (default: today). Release dates come from a --metadata snapshot or --online; packages without date information are skipped.

[vulnerabilities]

[vulnerabilities]
fail_on = "high"   # none | low | medium | high | critical

Matches at or above the threshold are errors, lower ones warnings. Severity comes from CVSS v3 vectors (computed base score) or the advisory's database_specific.severity; advisories with no severity count as medium. none turns every vulnerability into a warning.

[hygiene]

[hygiene]
duplicates = "warn"    # off | warn | error - same package at several versions in one lockfile (npm, cargo)
unpinned   = "warn"    # loose specifiers: requirements without ==, `*`/`latest`/open-ended npm ranges, URL/git refs
typosquat  = "error"   # one edit away from a top-1000 PyPI/npm name

Checks

depaudit audit --checks license,packages,vuln,hygiene,age,outdated runs a subset (default: all). vuln needs --osv.