Supply Chain Security¶
Cargo.lock pins ~530 transitive dependencies. Two tools stand
between a routine dependency bump and pulling in a compromised
crate. They apply at different moments.
cargo-deny: known-bad packages¶
deny.toml (via
cargo-deny)
carries an explicit [bans] deny list of specific
package/version IOCs from public supply-chain reports. Also
checked: the RustSec advisory database and a source restriction
(crates.io only, no git or path dependencies).
Seeded from the arrayref@0.3.10 compromise
(Wiz, 2026-08-20):
nine package names (three hijacked releases, six typosquats)
are blocked.
This check reads Cargo.lock and reports. It never rewrites
the lockfile and never compiles anything.
When a new report names package/version IOCs, add them to
deny.toml's [bans].deny list.
cargo-cooldown: minimum publish age¶
Most supply-chain compromises get caught within hours to days.
cooldown.toml (via
cargo-cooldown)
holds a 7-day minimum publish age gate.
Only applies when the lockfile is refreshed. With
lockfile-baseline = "floor", versions already in Cargo.lock
are the baseline; it says nothing about what's already locked.
Scripts¶
Three scripts, available from the partylinepager.sh
Maintenance menu (items 5, 6, 7) or standalone:
hooks/audit-cargo-deps.sh # read-only: cargo deny check
hooks/update-cargo-deps.sh # rewrites Cargo.lock, then audits,
# restores on failure
hooks/triage-advisory.sh # diagnose failures, add ignores
audit-cargo-deps.sh (menu item 5)¶
Read-only. Runs cargo deny --locked check advisories bans
sources. The --locked flag prevents cargo from rewriting
Cargo.lock.
update-cargo-deps.sh (menu item 6)¶
Snapshots Cargo.lock before starting and restores it on every
failure path including Ctrl-C. Leaves either a lockfile that
passed the audit or the one you started with, never one that
failed.
triage-advisory.sh (menu item 7)¶
Diagnoses advisory failures and helps resolve them. For each RUSTSEC finding it:
- Checks whether
cargo update -p <pkg>can resolve it - If yes: directs you to update-cargo-deps.sh (menu item 6)
- If blocked (transitive dep constraint): offers to add an
ignore entry to
deny.tomlwith a justification comment
Pass -y for non-interactive mode (auto-accepts ignores).
All three run inside compose/docker-compose.build-tools.yml,
the build-stage container. Each tool is pinned to an exact
version and installed under its own --root in
.cache/cargo/pinned on first run.
Handling advisory failures¶
New RustSec advisories are published continuously. When the audit fails in CI on an advisory, this is the procedure:
Quick version¶
./hooks/triage-advisory.sh
# follow the prompts
git add deny.toml && git commit -m "Ignore RUSTSEC-YYYY-NNNN"
git push
Step by step¶
-
Run triage (Maintenance menu item 7, or standalone). The script runs the audit, parses each finding, and checks whether a compatible update exists.
-
If an update is available: the script tells you to run update-cargo-deps.sh (menu item 6). That updates
Cargo.lock, respects the cooldown gate, and re-audits. -
If blocked (common): the advisory fix requires a version outside the semver range allowed by a parent dependency. Example:
imbl-sized-chunks 0.2.0is the fix, butimbl 6.1.0pins^0.1.3. No amount ofcargo updatecan cross that boundary. The triage script offers to add an ignore entry todeny.toml. -
Commit and push
deny.toml(andCargo.lockif updated). The CI pipeline will pass. -
Revisit later. Ignored advisories stay in
deny.tomluntil the upstream blocker is resolved. When the parent crate releases a version that accepts the fix, remove the ignore and run update-cargo-deps.sh.
Why blocking is common¶
This project depends on matrix-sdk, which pulls ~200
transitive crates. When an advisory hits a transitive dep
three levels deep, the fix often requires a new major version
of an intermediate crate, which requires matrix-sdk to update
its own dependency. That upstream release is outside your
control.
Ignoring the advisory is the correct, secure choice when:
- The advisory affects code paths you don't exercise
- The fix is published but blocked by a transitive dep
- You've verified the advisory and assessed the risk
The ignore entry in deny.toml documents the decision. The
comment explains why. When the blocker clears, the ignore is
removed and the dep is updated.
Manual triage (without the script)¶
If you prefer to do it by hand:
- Run
./hooks/audit-cargo-deps.shand read the output - Note the RUSTSEC ID and package name
- Try:
docker compose -f compose/docker-compose.build-tools.yml run --rm -T build-tools cargo update -p <pkg> --dry-run - If "Locking 0 packages": the update is blocked. Add to
deny.toml:
- If an update shows: run
./hooks/update-cargo-deps.sh
Not a supply-chain issue¶
Compiler deprecation warnings (future-incompat-report)¶
Messages like "the following packages contain code that will be rejected by a future version of Rust" are compiler warnings, not security advisories. They do not fail the audit and do not need ignore entries.
See Building and Testing > Compiler warnings for how to read the report and what to do about them.
Build failures after Rust version bump¶
If you update the FROM rust: line in the Dockerfile and
the build fails on a transitive dep, that is a toolchain
compatibility issue, not a supply-chain issue. Either pin
the Rust version back or wait for upstream to fix the dep.
The supply-chain scripts do not handle this.