Skip to content

Commit 7533b74

Browse files
authored
ci: harden PR pipeline (audit, MSRV, beta matrix, release + docker smoke) (#57)
* ci: harden PR pipeline (audit, MSRV, release build, beta matrix, docker smoke) Restructure the single stable-only fmt+clippy+test job into separate jobs: fmt+clippy (stable), a test matrix on stable+beta (beta non-blocking), build --release, cargo audit, an MSRV check pinned to Rust 1.91, and a Docker build smoke test mirroring the release workflow. Add concurrency cancellation for superseded PR runs and per-job timeouts. Declare rust-version = "1.91" in [workspace.package] and inherit it in every member crate so the stated MSRV is a real, CI-enforced contract. * ci: disable credential persistence on checkout steps Set persist-credentials: false on all six actions/checkout steps. These jobs run untrusted PR code via cargo and docker build, and checkout's default leaves the GITHUB_TOKEN in the local git config where that code could read it. None of the jobs push or otherwise need git credentials. * fix(deps): bump rustls-webpki to 0.103.13 Clears three advisories flagged by the new cargo audit job: a reachable panic in CRL parsing and two name-constraint bypasses (wildcard and URI). Semver-compatible patch, lockfile only, no manifest or code change. cargo also corrects a stale lockfile reference while rewriting Cargo.lock: data-encoding-macro-internal 0.1.17 (a build-time macro crate) requires syn 1.x, and its dependency entry is re-pinned from syn 2.0.117 to the 1.0.109 it actually resolves to. Unrelated to the bump, unavoidable when cargo touches the lock, and the more-correct state. * ci(audit): suppress no-fix advisories with justification and a re-check The cargo audit job stays hard-fail. The remaining three advisories have no available upstream fix, so they are suppressed in .cargo/audit.toml (the location cargo-audit actually reads; a repo-root audit.toml is not read), each with an inline rationale and removal condition: - RUSTSEC-2026-0118/0119 (hickory-proto 0.25.2, via libp2p-dns 0.44): reachable, since the node wraps QUIC in the system DNS resolver. No fix until libp2p-dns ships hickory 0.26. Accepted, reachable risk. - RUSTSEC-2023-0071 (rsa Marvin): in the lockfile via sqlx's unconditional sqlx-mysql, but not linked (sqlx is postgres-only), so no RSA oracle exists; no upstream fix regardless. Two controls keep the suppression honest. The PR audit job echoes the active ignore list to the job summary, so a green check is not read as advisory-clean. A weekly scheduled job re-runs cargo audit without the ignores for visibility and hard-fails if hickory-proto moves off 0.25.x while the ignores remain, so the suppression cannot silently outlive the upstream fix. * ci(audit): reference tracking issue #76 in the hickory ignores * ci(audit): treat hickory-proto removal as drift too The scheduled drift guard only failed when hickory-proto was present and off 0.25.x, so removing it from Cargo.lock entirely left the RUSTSEC-2026-0118 /0119 ignores in place with the guard still passing. Treat an absent version as drift as well, so dead ignore entries are caught whether the dependency is bumped past 0.25.x or removed.
1 parent abdc775 commit 7533b74

10 files changed

Lines changed: 261 additions & 6 deletions

File tree

‎.cargo/audit.toml‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# cargo-audit configuration.
2+
#
3+
# cargo-audit reads its config from .cargo/audit.toml (relative to the working
4+
# directory) or ~/.cargo/audit.toml. It does NOT read a repo-root audit.toml,
5+
# and there is no CLI flag to point at a config file (`-f` is the lockfile path).
6+
# This file lives at .cargo/audit.toml for that reason.
7+
#
8+
# The audit job stays hard-fail: only the advisories explicitly listed below are
9+
# suppressed, so any NEW advisory still breaks CI. Each entry below has no
10+
# available upstream fix; everything fixable is fixed in Cargo.lock instead.
11+
#
12+
# Tracking: the hickory ignores are reachable accepted risk. A scheduled re-check
13+
# (.github/workflows/audit-schedule.yml) runs without these ignores and fails if
14+
# the lockfile moves off the pinned vulnerable versions while the ignores remain.
15+
16+
[advisories]
17+
ignore = [
18+
# hickory-proto 0.25.2 (pulled by libp2p-dns 0.44 -> hickory-resolver 0.25).
19+
# REACHABLE: the node wraps its QUIC transport in the system DNS resolver
20+
# (crates/gitlawb-node/src/p2p/mod.rs:238), so a malicious DNS response can
21+
# trigger these DoS paths. No fix is available: the latest libp2p-dns on
22+
# crates.io is 0.44 and pins hickory 0.25; the fix requires hickory >=0.26.1.
23+
# REMOVE both once libp2p-dns ships a release using hickory 0.26 (verified
24+
# 2026-06-21: cargo update -p hickory-proto locks 0 packages; still 0.25.2).
25+
# Tracking: https://github.com/Gitlawb/node/issues/76
26+
"RUSTSEC-2026-0118", # hickory NSEC3 closest-encloser unbounded loop
27+
"RUSTSEC-2026-0119", # hickory O(n^2) name-compression CPU exhaustion
28+
29+
# rsa 0.9.10 (Marvin timing sidechannel). Present in Cargo.lock because sqlx
30+
# resolves sqlx-mysql unconditionally, and sqlx-mysql depends on rsa. cargo
31+
# audit scans the lockfile, so it flags rsa even though it is NOT linked into
32+
# our binary: sqlx is built postgres-only (crates/gitlawb-node/Cargo.toml),
33+
# so the MySQL RSA auth path is never compiled and there is no RSA oracle to
34+
# attack. No upstream fix exists regardless. REMOVE this ignore if sqlx is
35+
# ever built with the `mysql` feature, or any other consumer of rsa enters
36+
# the build, at which point it becomes a real reachable advisory.
37+
"RUSTSEC-2023-0071", # rsa Marvin attack (no fix; not linked in our build)
38+
]
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Scheduled Audit
2+
3+
# Self-expiring suppression check (companion to the PR-blocking audit gate in
4+
# pr-checks.yml). Runs weekly. It re-runs cargo audit WITHOUT the .cargo/audit.toml
5+
# ignore list to surface the full advisory set for visibility, and it hard-fails
6+
# if the lockfile has moved off the pinned vulnerable versions while the ignores
7+
# are still present, i.e. the upstream fix landed but the ignore was not dropped.
8+
on:
9+
schedule:
10+
- cron: "0 6 * * 1" # Mondays 06:00 UTC
11+
workflow_dispatch:
12+
13+
permissions:
14+
contents: read
15+
16+
env:
17+
CARGO_TERM_COLOR: always
18+
19+
jobs:
20+
scheduled-audit:
21+
name: scheduled audit (no ignores)
22+
runs-on: ubuntu-latest
23+
timeout-minutes: 30
24+
steps:
25+
- name: Check out repository
26+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
27+
with:
28+
persist-credentials: false
29+
30+
- name: Set up Rust toolchain
31+
uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # stable
32+
with:
33+
toolchain: stable
34+
35+
- name: Cache cargo
36+
uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
37+
with:
38+
key: audit-schedule
39+
40+
- name: Install cargo-audit
41+
run: cargo install --locked cargo-audit
42+
43+
# Full advisory report with NO suppressions. Run from a scratch dir that
44+
# has no .cargo/audit.toml so the ignore list does not apply; never fail
45+
# the build on this step, it is visibility only.
46+
- name: Full audit report (ignores not applied)
47+
run: |
48+
mkdir -p "$RUNNER_TEMP/full-audit"
49+
cp Cargo.lock "$RUNNER_TEMP/full-audit/Cargo.lock"
50+
cd "$RUNNER_TEMP/full-audit"
51+
{
52+
echo '### Full cargo audit (no ignores applied)'
53+
echo '```'
54+
cargo audit -f Cargo.lock || true
55+
echo '```'
56+
} >> "$GITHUB_STEP_SUMMARY"
57+
58+
# Drift guard: the hickory ignores are only valid while hickory-proto is
59+
# pinned at 0.25.x. If the lockfile has moved past that, the upstream fix
60+
# is in and the ignores in .cargo/audit.toml must be removed. Fail loudly.
61+
- name: Fail if hickory moved past 0.25.x while ignores remain
62+
run: |
63+
set -euo pipefail
64+
version="$(grep -A1 'name = "hickory-proto"' Cargo.lock | grep '^version' | head -1 | cut -d'"' -f2)"
65+
echo "hickory-proto in Cargo.lock: ${version:-not present}"
66+
ignores_present=false
67+
if grep -q 'RUSTSEC-2026-011' .cargo/audit.toml; then
68+
ignores_present=true
69+
fi
70+
# Drift = the lockfile no longer matches what the ignores assume:
71+
# hickory moved off 0.25.x (fix shipped) OR was removed entirely
72+
# (dead ignore entries left behind). Both must fail.
73+
if [ "$ignores_present" = true ] && { [ -z "${version}" ] || [[ "${version}" != 0.25.* ]]; }; then
74+
echo "::error::hickory-proto is ${version:-absent from Cargo.lock} but the RUSTSEC-2026-0118/0119 ignores are still in .cargo/audit.toml. The upstream fix appears to be available or the dependency is gone; remove the ignores."
75+
exit 1
76+
fi
77+
echo "No drift: ignore list is consistent with the pinned hickory-proto version."

‎.github/workflows/pr-checks.yml‎

Lines changed: 137 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,25 @@ on:
99
permissions:
1010
contents: read
1111

12+
# Cancel superseded runs on the same PR/branch; never cancel runs on main.
13+
concurrency:
14+
group: pr-checks-${{ github.ref }}
15+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
16+
1217
env:
1318
CARGO_TERM_COLOR: always
1419
RUST_BACKTRACE: 1
1520

1621
jobs:
17-
fmt-clippy-test:
18-
name: fmt + clippy + test
22+
fmt-clippy:
23+
name: fmt + clippy
1924
runs-on: ubuntu-latest
20-
25+
timeout-minutes: 30
2126
steps:
2227
- name: Check out repository
2328
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
29+
with:
30+
persist-credentials: false
2431

2532
- name: Set up Rust toolchain
2633
uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # stable
@@ -37,5 +44,132 @@ jobs:
3744
- name: cargo clippy
3845
run: cargo clippy --workspace --all-targets -- -D warnings
3946

47+
test:
48+
name: test (${{ matrix.toolchain }})
49+
runs-on: ubuntu-latest
50+
timeout-minutes: 45
51+
strategy:
52+
fail-fast: false
53+
matrix:
54+
toolchain: [stable, beta]
55+
# Beta is informational: an upstream beta regression should warn, not block merges.
56+
continue-on-error: ${{ matrix.toolchain == 'beta' }}
57+
steps:
58+
- name: Check out repository
59+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
60+
with:
61+
persist-credentials: false
62+
63+
- name: Set up Rust toolchain
64+
uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # stable
65+
with:
66+
toolchain: ${{ matrix.toolchain }}
67+
68+
- name: Cache cargo
69+
uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
70+
with:
71+
key: ${{ matrix.toolchain }}
72+
4073
- name: cargo test
4174
run: cargo test --workspace
75+
76+
build-release:
77+
name: build --release
78+
runs-on: ubuntu-latest
79+
timeout-minutes: 45
80+
steps:
81+
- name: Check out repository
82+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
83+
with:
84+
persist-credentials: false
85+
86+
- name: Set up Rust toolchain
87+
uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # stable
88+
with:
89+
toolchain: stable
90+
91+
- name: Cache cargo
92+
uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
93+
with:
94+
key: release
95+
96+
- name: cargo build --release
97+
run: cargo build --release --workspace
98+
99+
audit:
100+
name: cargo audit
101+
runs-on: ubuntu-latest
102+
timeout-minutes: 30
103+
steps:
104+
- name: Check out repository
105+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
106+
with:
107+
persist-credentials: false
108+
109+
- name: Set up Rust toolchain
110+
uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # stable
111+
with:
112+
toolchain: stable
113+
114+
- name: Cache cargo
115+
uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
116+
with:
117+
key: audit
118+
119+
- name: Install cargo-audit
120+
run: cargo install --locked cargo-audit
121+
122+
# Hard-fail gate. Suppressions live in .cargo/audit.toml (read automatically
123+
# from the repo root), each with no available upstream fix. A green check
124+
# here means "no NEW advisories", not "advisory-clean": surface the active
125+
# ignore list so a reviewer sees exactly what is being accepted.
126+
- name: Show active audit ignores
127+
run: |
128+
{
129+
echo '### cargo audit: accepted advisories (.cargo/audit.toml)'
130+
echo '```toml'
131+
sed -n '/^\[advisories\]/,$p' .cargo/audit.toml
132+
echo '```'
133+
} >> "$GITHUB_STEP_SUMMARY"
134+
135+
- name: cargo audit
136+
run: cargo audit
137+
138+
msrv:
139+
name: MSRV (Rust 1.91)
140+
runs-on: ubuntu-latest
141+
timeout-minutes: 30
142+
steps:
143+
- name: Check out repository
144+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
145+
with:
146+
persist-credentials: false
147+
148+
- name: Set up Rust 1.91
149+
uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # 1.91
150+
with:
151+
toolchain: "1.91"
152+
153+
- name: Cache cargo
154+
uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
155+
with:
156+
key: msrv
157+
158+
- name: cargo check on MSRV
159+
run: cargo check --workspace --all-targets
160+
161+
docker-build:
162+
name: Docker build smoke test
163+
runs-on: ubuntu-latest
164+
timeout-minutes: 30
165+
steps:
166+
- name: Check out repository
167+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
168+
with:
169+
persist-credentials: false
170+
171+
- name: Build image
172+
run: docker build -t gitlawb-node:ci-test .
173+
174+
- name: Smoke test --version
175+
run: docker run --rm gitlawb-node:ci-test --version

‎Cargo.lock‎

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ members = [
1111
[workspace.package]
1212
version = "0.3.9"
1313
edition = "2021"
14+
rust-version = "1.91"
1415
license = "MIT OR Apache-2.0"
1516
authors = ["gitlawb contributors"]
1617
repository = "https://github.com/gitlawb/node"

‎crates/git-remote-gitlawb/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name = "git-remote-gitlawb"
33
description = "Git remote helper — enables 'git clone gitlawb://did:gitlawb:...'"
44
version.workspace = true
55
edition.workspace = true
6+
rust-version.workspace = true
67
license.workspace = true
78

89
[[bin]]

‎crates/gitlawb-attest/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name = "gitlawb-attest"
33
description = "External Attestation v1: pluggable provenance attachments for gitlawb ref-update certs"
44
version.workspace = true
55
edition.workspace = true
6+
rust-version.workspace = true
67
license.workspace = true
78

89
[dependencies]

‎crates/gitlawb-core/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name = "gitlawb-core"
33
description = "Core cryptographic primitives for the gitlawb network: DIDs, CIDs, UCAN, HTTP Signatures, ref-update certificates"
44
version.workspace = true
55
edition.workspace = true
6+
rust-version.workspace = true
67
license.workspace = true
78

89
[dependencies]

‎crates/gitlawb-node/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name = "gitlawb-node"
33
description = "The gitlawb node daemon — git hosting over HTTP with DID auth"
44
version.workspace = true
55
edition.workspace = true
6+
rust-version.workspace = true
67
license.workspace = true
78

89
[[bin]]

‎crates/gl/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name = "gl"
33
description = "The gitlawb CLI — identity management, node control, MCP server"
44
version.workspace = true
55
edition.workspace = true
6+
rust-version.workspace = true
67
license.workspace = true
78

89
[[bin]]

0 commit comments

Comments
 (0)