Gate the Rust dependency graph with cargo deny, and watch it with Dependabot and Trivy

Merged
JE
2mo ago
+170-7

Second of three CI-hardening pull requests for the Rust crate (card: hey-sdk Rust: CI hardening). Stacked on #155; the diff here is only the dependency-security layer. Merge #155 first.

What changes

rust/deny.toml is one cargo deny policy for both Cargo workspaces (rust/ and conformance/runner/rust):

  • advisories: RustSec, yanked crates denied, no ignores (each future entry carries the id, why it does not apply, and when to look again).
  • licenses: an allow-list of real SPDX identifiers (MIT, Apache-2.0, Apache-2.0 WITH LLVM-exception, BSD-2-Clause, BSD-3-Clause, ISC, Unicode-3.0, Zlib, MPL-2.0, CDLA-Permissive-2.0, OpenSSL) — the fleet's list from the cross-repo Rust devex standard, not tuned to this tree, so an entry nothing uses yet is not a finding. The unpublished crates (generator, runner) are the repository's MIT and are skipped.
  • bans: multiple-versions = "warn" — duplicates are a signal, not a defect (today: base64 0.22/0.23, syn 2/3, winnow 0.7/1); openssl-sys denied since TLS is rustls; wildcard versions denied except the runner's path dependency.
  • sources: crates.io only.

make rs-deny runs it over both workspaces (the runner with --config ../../../rust/deny.toml), and make rs-check now ends with it, so test-rust installs cargo-deny first (taiki-e/install-action, SHA-pinned, checksum-verified binary). CONTRIBUTING lists the tool.

Dependabot gets a grouped cargo entry for /rust and /conformance/runner/rust, with the same cooldowns as the Go entries.

security.yml gets trivy-rust beside trivy-go: Trivy reads each workspace's Cargo.lock (rust/ and conformance/runner/rust, uploaded under separate SARIF categories) and fails on HIGH/CRITICAL as the Go job does. It is the second reader of the same RustSec data on purpose — on a PR cargo deny has already failed on the same advisory, and the weekly run here is what puts findings on the Security tab — rather than a third tool: cargo audit reads the same database and was left out.

Verified locally: make rs-deny green on both workspaces (advisories, bans, licences, sources all ok, three duplicate-version warnings); make rs-check green; actionlint and zizmor clean apart from the pre-existing self-repository note.

Merge order

This PR is part of a five-PR stack, each rebased on the one below and all five on current main, so they merge cleanly in this order and only this order: #155 (build/MSRV/feature matrix) → #157 (cargo deny, Dependabot, Trivy) → #159 (semver-checks, packaging) → #160 (docs, examples, rustfmt) → #162 (crates.io publishing). Every one of them touches .github/workflows/test.yml and/or the Makefiles; the stacking is the conflict resolution. Merge each one after the one below has landed; GitHub retargets the next PR to main when the merged branch is deleted. #162 can merge before the crates.io bootstrap: it publishes nothing until the crate exists on crates.io (its publish job fails closed with instructions on a tag until then), and nothing in it changes after the bootstrap.