ADR-0917: cargo-deny supply-chain policy enforcement¶
- Status: Accepted
- Date: 2026-05-31
- Deciders: Lusoris
- Tags:
security,ci,rust,supply-chain,license
Context¶
The fork's Rust workspace (bindings/rust/vmafx-sys, core/src/feature/rust/tad) currently has no automated supply-chain gate. Today nothing prevents a transitive dependency from pulling in:
- a GPL/AGPL/SSPL crate that contaminates the BSD-3-Clause-Plus-Patent shipping license,
- a banned-by-policy crate such as a second TLS stack (
openssl-sys/native-tls) when we already link OpenSSL on the C side and prefer rustls in Rust, - a known-vulnerable crate version flagged in the RustSec advisory DB,
- a crate fetched from an unknown git registry or unpinned
git = "…"source.
The Rust pilot crates are small today (vmafx-sys + vmafx-tad), but Phase 4 of the rebrand adds more Rust surfaces. Wiring the gate now — while the dep graph is shallow — gives us a clean baseline; retrofitting later means triaging dozens of legacy duplicates.
cargo-deny is the de-facto enforcement tool for these four concerns (licenses, bans, advisories, sources) and integrates cleanly with the existing rust-ci.yml workflow. The tool is permissively licensed (Apache-2.0 OR MIT) and used by the Rust Foundation, Bevy, Tokio, and others.
Decision¶
We will adopt cargo-deny with a deny.toml at the workspace root that:
- Licenses: explicit allowlist of permissive licenses only (
Apache-2.0,Apache-2.0 WITH LLVM-exception,BSD-3-Clause,ISC,MIT,Unicode-3.0,Unlicense). Everything else is rejected by default.MPL-2.0is allowed narrowly forcbindgen(build-time tool, no shipping linkage). Workspace crates withoutpublish = falsepreviously failed because cargo-deny's SPDX parser does not yet recogniseBSD-3-Clause-Plus-Patent; we discharge this by addingpublish = falsetovmafx-tad, which then falls under[licenses.private] ignore = true. - Bans: deny
openssl-sysandnative-tls(rustls preferred); wildcards (*) denied; multiple-versions surfaced as warnings (so we see the duplicate-dep surface without failing CI on routine transitive churn). - Advisories: RustSec database; vulnerabilities and unsound findings fail the gate (schema v2 default); unmaintained and yanked crates warn-only.
- Sources: only
crates.ioallowed; unknown registries and unknown git sources fail the gate.
The check runs as a new cargo-deny job in rust-ci.yml, in parallel with the existing vmafx-sys build/test job, via the official EmbarkStudios/cargo-deny-action@v2.0.20 pinned to its commit SHA.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
cargo-deny (chosen) | Single tool covers all 4 concerns; widely adopted; declarative TOML config; pinned action with SHA. | Yet another CI step; SPDX parser lags behind the registry (forced the publish = false workaround). | — |
cargo audit only | Smaller scope, easier to adopt. | Covers advisories only — leaves licenses, bans, and sources unchecked. | Insufficient coverage. |
| GitHub Dependabot + Dependency Review action | Native to GitHub; zero config. | Covers advisories + license review, but Dependency Review only triggers on PRs against the default branch (misses pushes to feature branches); no opinionated ban support; no source-pinning. | Misses the bans + sources surface. |
cargo-vet | Crowdsourced trust audits, stronger supply-chain story than cargo-deny. | Requires an audit-set bootstrap and ongoing maintainer attention. Better as a later layer on top. | Out of scope for the initial gate; revisit when the Rust surface grows past ~20 crates. |
| Do nothing | No CI cost. | Leaves the four supply-chain risks unmitigated; retrofitting is harder once the dep graph deepens. | Unacceptable risk for a security-sensitive fork. |
Consequences¶
- Positive:
- Every PR touching Rust gets license, ban, advisory, and source enforcement before merge.
- License drift is caught at PR time instead of release-prep time.
- Future Rust pilots inherit the policy automatically (no per-crate opt-in required).
- Pins the dep-graph topology so adding
openssl-sys(or any other banned crate) requires an explicit ADR-cited exception. - Negative:
- One extra CI job per Rust-touching PR (~20-30 s on cached runs).
- When upstream Rust crates change their license (rare), the gate will fail until
deny.tomlis updated. BSD-3-Clause-Plus-Patentis not an SPDX short identifier that cargo-deny's parser recognises today — we worked around it by makingvmafx-tadpublish = false. If a future workspace crate needs to publish with that license, it will need a[[licenses.clarify]]entry with aLICENSEfile hash anchor.- Neutral / follow-ups:
bindings/rust/vmafx-sys/Cargo.tomlalready declaresBSD-3-Clause; no change needed there. We are not addingpublish = falseto vmafx-sys because the policy intent for it may differ (FFI shim could plausibly be published as a public crate later); the current SPDX string already validates.- Three duplicate-version warnings exist today (
linux-raw-sys,rustix,windows-sys— all transitive throughbindgen/cbindgen). These are warn-only and not actionable until bindgen 0.69 → 0.70+ migration aligns itsrustixminor. Tracking via this ADR's References section; no separate issue. - Doc:
docs/development/cargo-deny.mdcovers running the gate locally and triaging findings.
References¶
- Source:
req— paraphrased: the user requested wiringcargo-denyfor Rust supply-chain policy enforcement covering licenses, banned crates, vulnerability scanning, duplicate-dep detection, and source pinning. - cargo-deny user guide
- SPDX license list
- RustSec advisory database
- Related: ADR-0535 — ADR atomic allocator
- Related: ADR-0628 — remote-aware allocator
- Initial findings:
cargo deny checkon this PR reportsadvisories ok, bans ok, licenses ok, sources ok; 3 duplicate-version warnings (warn-only).