Dependency Security & License Compliance

This document describes Paladin's supply-chain security tooling: vulnerability scanning, license compliance, the exception process, and Software Bill of Materials (SBOM) generation. It is part of Milestone 10 — CI Hardening and Release Automation, Epic 2.

Tooling Overview

ConcernToolWhere it runsConfig / source of truth
Known vulnerabilities (RustSec)cargo auditCI (security-audit job) + local.cargo/audit.toml
Known vulnerabilities (OSV DB)OSV-ScannerCI (osv-scanner job, PR annotations)Cargo.lock
License compliance + bans + duplicatescargo denyCI (cargo-deny job) + localdeny.toml
Software Bill of Materialscargo cyclonedxRelease pipelineCargo.lock

Running the Checks Locally

# Vulnerability advisories (reads exceptions from .cargo/audit.toml)
cargo audit

# License policy, bans, duplicate versions, advisories (reads deny.toml)
cargo deny check

# Both at once
make security

# Generate a CycloneDX SBOM for the workspace
make sbom

Install the tools once with:

cargo install --locked cargo-audit cargo-deny cargo-cyclonedx

License Policy

deny.toml enforces a permissive-only allow-list:

  • Allowed (core): MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, Zlib.
  • Allowed (additional permissive, each justified in deny.toml): Unicode-3.0, 0BSD, CC0-1.0, CDLA-Permissive-2.0.
  • Strong copyleft licenses (GPL-*, AGPL-*, LGPL-*) are not allowed.
  • Weak/file-level copyleft (MPL-2.0) is not in the global allow-list; it is granted only via narrowly-scoped per-crate [[licenses.exceptions]] entries so the global policy stays permissive-only.

If a required dependency uses a license outside this set, do not disable the license check. Instead, either:

  1. Add the specific SPDX license id to deny.toml's [licenses].allow list with a comment justifying it (for genuinely permissive licenses), or
  2. Add a narrowly-scoped [[licenses.exceptions]] entry granting a specific license to a specific crate (preferred for weak copyleft like MPL-2.0), or
  3. Add a [[licenses.clarify]] entry for a specific crate when its license metadata is ambiguous.

Advisory Exception Process

Some advisories cannot be remediated immediately (typically transitive or dev/test-only dependencies with no upstream fix). Exceptions are recorded in two synchronized files:

  • .cargo/audit.toml — auto-discovered by cargo audit.
  • deny.toml ([advisories].ignore) — used by cargo deny.

Each exception must include a comment stating:

  1. The advisory ID (e.g. RUSTSEC-2023-0071).
  2. The affected crate and why it is in the tree (e.g. transitive dev dependency of sqlx-mysql).
  3. Why it is not yet fixable (no upstream patch available).
  4. A revisit condition (e.g. "revisit when sqlx upgrades rsa").

When adding or removing an exception, update both files so the two scanners do not contradict each other.

Current tracked exceptions:

  • RUSTSEC-2023-0071 — RSA timing side-channel via rsa 0.9.x (transitive dev/test dep of sqlx-mysql; no upstream fix).
  • RUSTSEC-2025-0111tokio-tar path traversal (transitive dev/test dep of testcontainers; no upstream fix).

OSV-Scanner Policy

OSV-Scanner runs on pull requests and reports findings as PR annotations (via SARIF upload). It is currently annotate-only (non-blocking) to avoid contradicting the cargo audit gate while the annotation signal level is assessed. It may be promoted to a blocking gate later (see PRD Open Question 1).

Snyk Evaluation & Decision

Decision: Deferred.

Snyk's free tier was evaluated against the combined coverage of cargo audit (RustSec), OSV-Scanner (OSV database), and cargo deny (licenses + bans + duplicates):

Capabilitycargo audit + OSV + cargo denySnyk free tier
RustSec advisoriesYes (cargo audit)Yes
Broad OSV coverageYes (OSV-Scanner)Partial
License complianceYes (cargo deny)Limited on free tier
Dependency bans / duplicatesYes (cargo deny)No
Reachability analysisNoYes (added value)
Automated fix PRsNoYes (added value)
Requires external account/secretNoYes (SNYK_TOKEN)
Maintenance costLow (all in-repo config)Medium (account + secret rotation)

Rationale: The existing three tools already cover advisories and license compliance with no external account, no secret management, and fully version-controlled policy (.cargo/audit.toml, deny.toml). Snyk's incremental value (reachability analysis, automated fix PRs) does not currently justify the added account/secret-management overhead.

Revisit when: the project needs reachability-based prioritization of advisories, wants automated dependency-bump PRs beyond Dependabot, or an enterprise compliance requirement mandates Snyk specifically.

SBOM

Every GitHub release attaches a CycloneDX SBOM (paladin-<version>.cdx.json) generated from the locked dependency graph by the sbom job in .github/workflows/release.yml. Generate the SBOMs locally with make sbom, which runs cargo cyclonedx --all --format json and writes one <crate>.cdx.json next to each workspace crate's manifest (the root package's paladin-ai.cdx.json is the primary deliverable). These generated files are git-ignored.