From 52ee1f190cab3de3cc469164409620f6e4a4e7b3 Mon Sep 17 00:00:00 2001 From: Tin Dang Date: Tue, 14 Jul 2026 09:25:08 +0700 Subject: [PATCH] ci(supply-chain): wire cargo audit + cargo deny into a blocking CI gate deny.toml existed in the tree but was never invoked by any workflow -- its own header comment said so (SUPPLY-01 in docs/PRODUCTION-CONTRACT.md). Adds .github/workflows/supply-chain.yml with two ubuntu-latest jobs: `audit` (cargo audit, hard-fails on RUSTSEC Vulnerability-class advisories only -- unmaintained/unsound/yanked stay visible-but-non-blocking, cargo audit's own default) and `deny` (cargo deny check advisories licenses bans sources). Triggers: PRs touching Cargo.toml/Cargo.lock/deny.toml/ .cargo/audit.toml, push to main, and a weekly cron (advisories publish independent of code changes). Runs on the hosted runner, not the self-hosted moon-dev box -- this only inspects dependency metadata, no build needed. Fixed three real advisories in the lockfile to reach green: memmap2 0.9.10->0.9.11 (RUSTSEC-2026-0186, unsound pointer-offset validation), crossbeam-epoch 0.9.18->0.9.20 (RUSTSEC-2026-0204, invalid pointer deref in Display), spin 0.9.8->0.9.9 (yanked); bumped rust-embed 8.11.0->8.12.0 (console feature) which drops the unmaintained core2/proc-macro-error2 transitives in favor of no_std_io2/proc-macro-error3. All via `cargo update -p ` -- no Cargo.toml range changes needed, no functional code touched. Verified default, tokio+jemalloc, and console feature builds still compile clean after the bumps. Three unmaintained transitive advisories with no available safe upgrade today (fxhash via monoio 0.2.4, paste via tikv-jemalloc-ctl 0.6.1, rustls-pemfile pending a rustls-pki-types::PemObject migration) are explicitly ignore-listed with reasons in deny.toml and the new .cargo/audit.toml, rather than left to pass silently or leaving the gate permanently red. cargo-deny's advisory matcher additionally misses RUSTSEC-2026-0097 (rand 0.9.2 unsound, via metrics-util -- a narrow custom-logger reentrancy issue moon's tracing-subscriber setup can't trigger) that cargo-audit does catch; ignore-listed in .cargo/audit.toml with the same reasoning for when cargo-deny's matcher catches up. Local validation (cargo-audit 0.22.2, cargo-deny 0.19.6, pinned in the workflow via taiki-e/install-action): both `cargo audit` and `cargo deny check advisories licenses bans sources` exit 0 against the current Cargo.lock. task: #63 (SUPPLY-01) author: Tin Dang --- .cargo/audit.toml | 36 ++++++++++ .github/workflows/supply-chain.yml | 69 +++++++++++++++++++ CHANGELOG.md | 24 +++++++ Cargo.lock | 107 +++++++++++++---------------- deny.toml | 38 +++++++--- 5 files changed, 207 insertions(+), 67 deletions(-) create mode 100644 .cargo/audit.toml create mode 100644 .github/workflows/supply-chain.yml diff --git a/.cargo/audit.toml b/.cargo/audit.toml new file mode 100644 index 000000000..961dc0a53 --- /dev/null +++ b/.cargo/audit.toml @@ -0,0 +1,36 @@ +# cargo-audit configuration for Moon. +# Run: cargo audit +# +# CI: wired into .github/workflows/supply-chain.yml (job `audit`), a +# blocking check for SUPPLY-01. cargo-audit only fails the build on +# `Warning::Vulnerability`-class advisories by default (unmaintained / +# unsound / yanked print but do not fail) — deliberately NOT overridden +# with `--deny warnings` here, matching the design that a real CVE hard +# fails while informational advisories stay visible-but-non-blocking. The +# entries below are additionally silenced via `ignore` (same three +# unmaintained crates as deny.toml, plus one unsound-but-inapplicable +# advisory not caught by cargo-deny's older rustsec matcher) purely to +# keep CI logs clean; each has a reason and a re-check trigger. + +[advisories] +ignore = [ + # fxhash 0.2.1: unmaintained, no safe upgrade; pinned transitively by + # monoio 0.2.4 (vendor/monoio). Revisit when monoio drops it. + "RUSTSEC-2025-0057", + # paste 1.0.15: unmaintained (archived upstream), no safe upgrade; + # pinned transitively by tikv-jemalloc-ctl 0.6.1. Revisit on next + # jemalloc-ctl major bump. + "RUSTSEC-2024-0436", + # rustls-pemfile 2.2.0: unmaintained; migrating to + # rustls-pki-types::PemObject is a source-code change (src/tls*) out + # of scope for this CI-wiring change. Tracked as a follow-up. + "RUSTSEC-2025-0134", + # rand 0.9.2 (transitive via metrics-util 0.20.4 -> metrics -> moon): + # RUSTSEC-2026-0097 is a narrow unsoundness that only triggers when a + # *custom log::Log implementation* calls rand::rng() reentrantly from + # inside a log callback while ThreadRng reseeds — moon uses + # tracing-subscriber, not a custom `log` logger, so the trigger path + # is not reachable. No newer metrics-util is published yet that pulls + # rand >=0.9.3. Revisit on the next metrics-util bump. + "RUSTSEC-2026-0097", +] diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml new file mode 100644 index 000000000..f06084af5 --- /dev/null +++ b/.github/workflows/supply-chain.yml @@ -0,0 +1,69 @@ +name: Supply Chain + +# SUPPLY-01 (docs/PRODUCTION-CONTRACT.md, section A): `cargo audit` (RUSTSEC +# advisories) + `cargo deny check` (advisories/licenses/bans/sources) as a +# blocking CI gate. Runs on ubuntu-latest, NOT the self-hosted moon-dev +# runner -- this job only inspects Cargo.lock/dependency metadata, no build +# is required, so the hosted runner's queue time is a non-issue and it +# keeps the self-hosted VM free for the Check job. +on: + push: + branches: [main] + paths: + - 'Cargo.toml' + - 'Cargo.lock' + - 'deny.toml' + - '.cargo/audit.toml' + - '.github/workflows/supply-chain.yml' + pull_request: + branches: [main] + paths: + - 'Cargo.toml' + - 'Cargo.lock' + - 'deny.toml' + - '.cargo/audit.toml' + - '.github/workflows/supply-chain.yml' + schedule: + # Weekly, independent of code changes -- RUSTSEC advisories are + # published continuously, so even an untouched lockfile can go from + # green to red. + - cron: '0 6 * * 1' + workflow_dispatch: {} + +concurrency: + group: supply-chain-${{ github.ref }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + +jobs: + # -- cargo audit: RUSTSEC vulnerability advisories -------------------- + # Fails only on Vulnerability-class advisories by default (see + # .cargo/audit.toml for the rationale -- unmaintained/unsound/yanked are + # visible-but-non-blocking, matching cargo-audit's own default lint + # levels; nothing here overrides that with --deny warnings). + audit: + name: cargo audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: dtolnay/rust-toolchain@1.94.1 + - name: Install cargo-audit + uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2.83.2 + with: + tool: cargo-audit@0.22.2 + - run: cargo audit + + # -- cargo deny: advisories + license allowlist + ban rules + sources - + deny: + name: cargo deny + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: dtolnay/rust-toolchain@1.94.1 + - name: Install cargo-deny + uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2.83.2 + with: + tool: cargo-deny@0.19.6 + - run: cargo deny check advisories licenses bans sources diff --git a/CHANGELOG.md b/CHANGELOG.md index 96f0a8cd6..be8383f06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,30 @@ task #44). New rows for shipped-but-untracked guarantees: `CRASH-02` (37-cell cross-plane kill-9 matrix), `MEM-10X-01` (10× RAM G2 acceptance), `REPL-PLANES-01` (all-plane replication); new `REPL-SOAK-01` row gates the v0.7.0 tag on the 24h replication soak. GA-blocking gap now 17 rows. +### Added — supply-chain security CI gate: `cargo audit` + `cargo deny check` (task #63, SUPPLY-01) + +`deny.toml` existed in the tree but was never wired into CI (its own header +comment said so). Added `.github/workflows/supply-chain.yml`: two +`ubuntu-latest` jobs, `audit` (`cargo audit`, blocking on RUSTSEC +vulnerability-class advisories) and `deny` (`cargo deny check advisories +licenses bans sources`, blocking on any deny.toml violation), triggered on +PRs touching `Cargo.toml`/`Cargo.lock`/`deny.toml`/`.cargo/audit.toml`, +push to `main`, and a weekly schedule (advisories publish independent of +code changes). Runs on the hosted runner, not the self-hosted `moon-dev` +box — no build is required, just dependency-graph inspection. + +Fixed three real advisories to get to green: `memmap2` 0.9.10 → 0.9.11 +(RUSTSEC-2026-0186, unsound pointer-offset validation), +`crossbeam-epoch` 0.9.18 → 0.9.20 (RUSTSEC-2026-0204, invalid pointer +dereference in `Display`), `spin` 0.9.8 → 0.9.9 (yanked), and dropped the +`core2`/`proc-macro-error2` unmaintained transitives by bumping +`rust-embed` 8.11.0 → 8.12.0 (console feature). Three unmaintained +transitive advisories with no available safe upgrade +(`fxhash` via monoio, `paste` via tikv-jemalloc-ctl, `rustls-pemfile` +pending a `rustls-pki-types::PemObject` migration) are explicitly +ignore-listed with reasons in `deny.toml` / `.cargo/audit.toml` rather +than left to silently pass — an always-red gate is worse than none, but a +silently-permissive one is worse still. ### Fixed — legacy-mode (`--disk-offload disable`) graph WAL replay silently dropped the entire graph plane on kill-9 restart (task #60) diff --git a/Cargo.lock b/Cargo.lock index c37c3e8cd..76407ae0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -431,15 +431,6 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - [[package]] name = "core_affinity" version = "0.8.3" @@ -540,9 +531,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] @@ -703,7 +694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1303,9 +1294,9 @@ dependencies = [ [[package]] name = "include-flate" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a05fb00d9abc625268e0573a519506b264a7d6965de09bac13201bfb44e723d" +checksum = "48f173716febb1ad596c16ea5637b5f1790ea32de8e627493ff82bc73b0876ce" dependencies = [ "include-flate-codegen", "include-flate-compress", @@ -1313,12 +1304,12 @@ dependencies = [ [[package]] name = "include-flate-codegen" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c3c319a7527668538a8530c541e74e881e94c4f41e1425622d0a41c16468af" +checksum = "4a7875b62a72ad3f3203cdd8950d4cf9947db036030b974b8b37ceae90c8d8c0" dependencies = [ "include-flate-compress", - "proc-macro-error2", + "proc-macro-error3", "proc-macro2", "quote", "syn", @@ -1326,9 +1317,9 @@ dependencies = [ [[package]] name = "include-flate-compress" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0bd9ea81b94169d61c5a397e9faef02153d3711fc62d3270bcde3ac85380d9" +checksum = "44fbb9c5ccb9a5b67b4afa2974c27e5507ea1bf6d22828cef418e4dfaeca51dd" dependencies = [ "libflate", "zstd", @@ -1454,25 +1445,25 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libflate" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3248b8d211bd23a104a42d81b4fa8bb8ac4a3b75e7a43d85d2c9ccb6179cd74" +checksum = "cd96e993e5f3368b0cb8497dae6c860c22af8ff18388c61c6c0b86c58d86b5df" dependencies = [ "adler32", - "core2", "crc32fast", "dary_heap", "libflate_lz77", + "no_std_io2", ] [[package]] name = "libflate_lz77" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a599cb10a9cd92b1300debcef28da8f70b935ec937f44fcd1b70a7c986a11c5c" +checksum = "ff7a10e427698aef6eef269482776debfef63384d30f13aad39a1a95e0e098fd" dependencies = [ - "core2", "hashbrown 0.16.1", + "no_std_io2", "rle-decode-fast", ] @@ -1612,9 +1603,9 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmap2" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -1893,7 +1884,7 @@ dependencies = [ "serde", "serde_json", "sha1_smol", - "sha2 0.11.0", + "sha2", "slotmap", "smallvec", "socket2 0.6.3", @@ -1952,6 +1943,15 @@ dependencies = [ "memoffset 0.9.1", ] +[[package]] +name = "no_std_io2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" +dependencies = [ + "memchr", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -2225,22 +2225,22 @@ dependencies = [ ] [[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" +name = "proc-macro-error-attr3" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +checksum = "34e4dd828515431dd6c4a030d26f7eaed7dd4778226e9d2bb968d65ca4ec3d4d" dependencies = [ "proc-macro2", "quote", ] [[package]] -name = "proc-macro-error2" -version = "2.0.1" +name = "proc-macro-error3" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +checksum = "5ee475e440453418ff1335189eddf7101ba502cd818ab7ae04209bc83aa925aa" dependencies = [ - "proc-macro-error-attr2", + "proc-macro-error-attr3", "proc-macro2", "quote", "syn", @@ -2496,9 +2496,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" +checksum = "e9e7760e252aaba7b09f4be00e36476cf585bdb68a53552ac954cdf504ab4bc9" dependencies = [ "include-flate", "rust-embed-impl", @@ -2508,10 +2508,11 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" +checksum = "3bcfc4d6f53af43755f7a723e4b6b8794fcce052a178dd8c6c1dadc5f5343097" dependencies = [ + "mime_guess", "proc-macro2", "quote", "rust-embed-utils", @@ -2521,11 +2522,12 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" +checksum = "42ffa149f6aa81b58a5b3011d01a857c4ed12c7a732d2c51947a4c7c692185f0" dependencies = [ - "sha2 0.10.9", + "include-flate", + "sha2", "walkdir", ] @@ -2564,7 +2566,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2712,17 +2714,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", -] - [[package]] name = "sha2" version = "0.11.0" @@ -2820,9 +2811,9 @@ dependencies = [ [[package]] name = "spin" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" dependencies = [ "lock_api", ] @@ -2892,7 +2883,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3499,7 +3490,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/deny.toml b/deny.toml index 123f175fc..e6429345a 100644 --- a/deny.toml +++ b/deny.toml @@ -1,13 +1,18 @@ # cargo-deny configuration for Moon (v2 format). -# Run: cargo deny check +# Run: cargo deny check advisories licenses bans sources # -# CI: this config is NOT currently wired into any GitHub Actions job — run -# it locally / ad hoc. The "Lint" job in .github/workflows/ci.yml runs -# scripts/audit-unsafe.sh + scripts/audit-unwrap.sh (SAFETY-comment coverage -# and an unwrap ratchet), which is a different check and does not invoke -# `cargo deny` or `cargo audit`. -# TODO(ci): wire `cargo deny check` into a blocking CI job (tracked -# separately — not part of this change). +# CI: wired into .github/workflows/supply-chain.yml (job `deny`), which runs +# `cargo deny check advisories licenses bans sources` on every PR touching +# Cargo.toml/Cargo.lock/deny.toml, on push to main, and weekly. Companion +# job `audit` runs `cargo audit` against the same lockfile. Both are +# blocking (SUPPLY-01). +# +# advisories `ignore` entries below: cargo-deny 2.x has no separate +# severity knob for vulnerability/unmaintained/unsound/notice — every +# matched advisory is a hard error unless explicitly ignored here. Real +# vulnerabilities get fixed in the lockfile (see git log for `cargo update` +# commits); only advisories with genuinely no available upgrade are +# ignored, each with a reason and a re-check-by note. [graph] targets = [] @@ -18,7 +23,22 @@ no-default-features = false version = 2 db-path = "~/.cargo/advisory-db" db-urls = ["https://github.com/rustsec/advisory-db"] -ignore = [] +ignore = [ + # fxhash 0.2.1 (transitive via monoio 0.2.4 -> monoio-io-wrapper / + # monoio-rustls): unmaintained, no safe upgrade published upstream. + # monoio owns the fxhash pin; revisit when monoio bumps past it. + { id = "RUSTSEC-2025-0057", reason = "fxhash is unmaintained with no safe replacement; pinned transitively by monoio 0.2.4 (vendor/monoio) — revisit when monoio drops it" }, + # paste 1.0.15 (transitive via tikv-jemalloc-ctl 0.6.1): unmaintained, + # archived by upstream, no safe upgrade (the suggested forks — pastey / + # with_builtin_macros — are not drop-in without a tikv-jemalloc-ctl bump). + { id = "RUSTSEC-2024-0436", reason = "paste is unmaintained (archived upstream) with no safe upgrade; pinned transitively by tikv-jemalloc-ctl 0.6.1 — revisit on next jemalloc-ctl major bump" }, + # rustls-pemfile 2.2.0 (direct, optional dep behind runtime-tokio / + # runtime-monoio for TLS PEM loading): unmaintained, no safe drop-in + # upgrade in the 2.x line. Migrating to rustls-pki-types' PemObject API + # is a real code change (src/tls*), tracked separately — not done here + # to keep this CI-wiring change infra-only. + { id = "RUSTSEC-2025-0134", reason = "rustls-pemfile is unmaintained; migrating callers to rustls-pki-types::PemObject is a source-code change out of scope for CI wiring — tracked as a follow-up" }, +] [licenses] version = 2