From d599b1a56c4a27555a1758a989ae7b68e8691499 Mon Sep 17 00:00:00 2001 From: Greg von Nessi Date: Thu, 20 Aug 2026 11:52:12 +0100 Subject: [PATCH 1/3] Cap what a single fork pull request can spend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This workflow is reachable from a fork pull request and had neither a concurrency group nor a job timeout, so it inherited GitHub's defaults for both: unlimited concurrent runs, and **six hours** per job. On paid Blacksmith runners that is a real number rather than a theoretical one — 15 job runs at the six-hour ceiling is 90 compute-hours for a single push, and without a concurrency group a contributor pushing five times held five complete sets at once rather than superseding the previous one. The repo now requires approval for every external contributor's fork PR, so nothing external starts without a deliberate click. This is the second half: the approval gate decides *who* runs, and these two settings decide *how much* a run that has been approved can cost. It bounds our own pushes too, which is where most of the waste actually was. Sized against measurement rather than a guess. Over the last six runs the slowest job here is 4m25s; 20 minutes leaves generous headroom while turning the worst case from 90 compute-hours into about 5. A job that hits 20 minutes is not a slow job, it is a stuck one. --- .github/workflows/ci.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eca154a..d636865 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,14 @@ on: pull_request: branches: [main] +concurrency: + # A new push to the same PR or branch supersedes the run in flight. Without + # this every push queued a *fresh* full set of jobs alongside the old ones, + # so a branch pushed five times held five complete matrices at once — on + # paid Blacksmith runners, and with nothing reclaiming the superseded work. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 @@ -14,6 +22,12 @@ jobs: test: name: Test runs-on: blacksmith-2vcpu-ubuntu-2404 + # Capped because the GitHub default is **six hours** per job, and this + # workflow is reachable from a fork pull request. Measured over the last + # six runs the slowest job here is 4m25s, so 20 minutes is roughly 5x + # headroom while removing the runaway: a stuck or hostile job now costs + # 20 minutes of a paid runner rather than a working day of one. + timeout-minutes: 20 steps: - uses: actions/checkout@v6 @@ -35,6 +49,7 @@ jobs: lint: name: Lint runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 20 steps: - uses: actions/checkout@v6 @@ -55,6 +70,7 @@ jobs: docs: name: Documentation runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 20 steps: - uses: actions/checkout@v6 @@ -72,6 +88,7 @@ jobs: msrv: name: MSRV (1.88) runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 20 steps: - uses: actions/checkout@v6 with: @@ -89,6 +106,7 @@ jobs: wasm: name: WASM Build runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 20 steps: - uses: actions/checkout@v6 @@ -106,6 +124,7 @@ jobs: cli-tests: name: CLI Integration Tests runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 20 steps: - uses: actions/checkout@v6 with: @@ -126,6 +145,7 @@ jobs: coverage: name: Coverage runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 20 steps: - uses: actions/checkout@v6 with: @@ -154,6 +174,7 @@ jobs: swift-bridge: name: Swift Bridge runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 20 steps: - uses: actions/checkout@v6 @@ -177,6 +198,7 @@ jobs: security: name: Security Audit runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 20 steps: - uses: actions/checkout@v6 @@ -201,6 +223,7 @@ jobs: benchmark: name: Benchmark runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 20 steps: - uses: actions/checkout@v6 @@ -240,6 +263,7 @@ jobs: feature-matrix: name: Feature Matrix (${{ matrix.features || 'no features' }}) runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 20 strategy: fail-fast: false matrix: From 71fe87cb1245d38a596ddc011bf7bc18bfabb80c Mon Sep 17 00:00:00 2001 From: Greg von Nessi Date: Thu, 20 Aug 2026 12:21:29 +0100 Subject: [PATCH 2/3] Clear two months of drift so the gate can be green again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neither failure was caused by the timeout change in this branch — both failed well inside the new 20-minute cap, and both fail on any cdx-core commit made today. `main` last ran in June and has not run since, so the drift accumulated where nothing was looking. **Lint.** `clippy::manual_assert_eq` did not exist when this last passed; under `-D warnings` a new lint is a new failure. One site, `cdx-core/src/document/tests.rs:227`, and the line directly above it already used `assert_eq!`, so the fix is what the surrounding code was doing anyway. It also buys a real failure message: `assert!(len() == 1)` reports only that a bool was false, while `assert_eq!` prints both sides. **Security Audit.** Two advisories published since June, both against *transitive* crates — neither appears in any `Cargo.toml`, so this is a lockfile-only change with no manifest touched and no semver decision taken: - RUSTSEC-2026-0204, `crossbeam-epoch` 0.9.18 -> 0.9.20 (solution: >= 0.9.20) - RUSTSEC-2026-0185, `quinn-proto` 0.11.13 -> 0.11.17 (solution: >= 0.11.15) `cargo update` also adds `rand` 0.10.2 and `rand_pcg` 0.10.2 as new transitive entries of the newer `quinn-proto`; the existing `rand` 0.9.2 pin is untouched. Verified locally on rustc 1.97.1, running CI's own commands rather than approximations of them: `cargo audit --ignore RUSTSEC-2023-0071 --ignore RUSTSEC-2026-0037` exits 0, with only the five pre-existing *allowed* warnings (unsound and yanked) remaining, and `cargo clippy --workspace --all-targets --all-features -- -D warnings` is clean. The five allowed warnings are deliberately left alone: they were passing before, they are warnings rather than vulnerabilities, and folding them into a drift fix would hide a judgement call inside a mechanical change. --- Cargo.lock | 38 ++++++++++++++++++++++++++-------- cdx-core/src/document/tests.rs | 2 +- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b022b15..14b2db5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -419,6 +419,7 @@ dependencies = [ "cfg-if", "cipher", "cpufeatures 0.3.0", + "rand_core 0.10.0", ] [[package]] @@ -650,9 +651,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", ] @@ -1120,11 +1121,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi", "wasip2", - "wasm-bindgen", ] [[package]] @@ -2069,7 +2068,7 @@ dependencies = [ "bit-vec", "bitflags", "num-traits", - "rand", + "rand 0.9.2", "rand_chacha", "rand_xorshift", "regex-syntax", @@ -2106,14 +2105,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.13" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83" dependencies = [ "bytes", - "getrandom 0.3.4", + "getrandom 0.4.1", "lru-slab", - "rand", + "rand 0.10.2", + "rand_pcg", "ring", "rustc-hash", "rustls", @@ -2164,6 +2164,17 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.1", + "rand_core 0.10.0", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -2198,6 +2209,15 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.0", +] + [[package]] name = "rand_xorshift" version = "0.4.0" diff --git a/cdx-core/src/document/tests.rs b/cdx-core/src/document/tests.rs index 3b8d214..bfc8ef1 100644 --- a/cdx-core/src/document/tests.rs +++ b/cdx-core/src/document/tests.rs @@ -224,7 +224,7 @@ fn test_extension_validation_mixed() { assert!(report.used_namespaces.contains(&"semantic".to_string())); assert!(report.used_namespaces.contains(&"forms".to_string())); assert_eq!(report.undeclared, vec!["forms"]); - assert!(report.warnings.len() == 1); + assert_eq!(report.warnings.len(), 1); } #[test] From 58c5cd016f24a2cdc9492819d66f778d504740c9 Mon Sep 17 00:00:00 2001 From: Greg von Nessi Date: Thu, 20 Aug 2026 12:31:57 +0100 Subject: [PATCH 3/3] Clear the unsound advisory cargo-deny errors on and cargo-audit only warns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit fixed what `cargo audit` was failing on and stopped there, because that is the command I checked. The job runs **two** tools and they do not agree: `cargo audit` treats an `informational = "unsound"` advisory as a warning, while `cargo deny` treats it as `error[unsound]`. So step 7 went green and step 9 stayed red on an advisory that had been sitting in audit's own output the whole time, counted among its "5 allowed warnings". RUSTSEC-2026-0190, `anyhow` — unsoundness in `Error::downcast_mut()`. The advisory gives `patched = [">= 1.0.103"]`; this moves 1.0.101 -> 1.0.104. `cdx-cli/Cargo.toml` already declares `anyhow = "1.0"`, so the caret range admits it and this stays lockfile-only, like the previous commit. The two configurations have drifted and this does not fix that. `ci.yml` passes `--ignore RUSTSEC-2023-0071 --ignore RUSTSEC-2026-0037` to audit, while `deny.toml` ignores only `RUSTSEC-2023-0071` and additionally sets `yanked = "warn"`. RUSTSEC-2026-0037 is ignored by one tool and unknown to the other, so the pair can disagree again on the next advisory. Left alone here: reconciling them is a policy decision about which tool is the source of truth, not a drift fix. Verified locally on rustc 1.97.1 with **both** of CI's commands this time, which is the check that was missing before: `cargo audit --ignore RUSTSEC-2023-0071 --ignore RUSTSEC-2026-0037` exits 0; `cargo deny check` reports "advisories ok, bans ok, licenses ok, sources ok"; and `cargo clippy --workspace --all-targets --all-features -- -D warnings` is clean. --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 14b2db5..c67ebf6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,9 +143,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.101" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "argon2"