Skip to content

ci: audit the dependency trees of the tooling the reusables install - #150

Merged
Jaro-c merged 1 commit into
mainfrom
ci/121-audit-tooling-trees
Aug 25, 2026
Merged

Jaro-c merged 1 commit into
mainfrom
ci/121-audit-tooling-trees

Conversation

@Jaro-c

@Jaro-c Jaro-c commented Aug 25, 2026

Copy link
Copy Markdown
Member

Closes the auditing half of Glyndor/apt#121. The containment half landed earlier — a job that installs third-party tooling holds no secrets, enforced by workflow-lint's tooling-isolation. This is the part that looks inside the trees.

The method, already proven by hand

cargo install --locked builds the tree in the tool's own bundled Cargo.lock, which ships inside the .crate on crates.io — not this repository's lockfile. So the audit downloads the .crate, extracts that lockfile, and runs cargo audit --file against it. That is the tree the runner actually builds.

Seven tools, versions read from the workflow files rather than hardcoded, sharing pin-watch.py's approach so a bump in one place cannot leave this auditing a version nobody installs.

What it finds today

tool version vulnerabilities yanked
cargo-audit 0.22.2 3 —
cargo-llvm-cov 0.8.7 2 —
cargo-cyclonedx 0.5.9 1 1
cargo-fuzz 0.13.2 1 —
cargo-semver-checks 0.50.0 1 —
cargo-about 0.9.2 0 —
cargo-deny 0.20.2 0 —

The three hand measurements from 2026-08-24 reproduce exactly — RUSTSEC-2026-0185 (quinn-proto, CVSS 7.5) in cargo-audit's own tree, RUSTSEC-2026-0009 (time) in cargo-cyclonedx's, and xml-rs 0.8.19 yanked. It found five more the hand sweep missed, because that covered three tools and this covers seven.

cargo-about reporting zero is the pin bump from earlier today working: 0.9.1 carried RUSTSEC-2026-0204, and 0.9.2 does not.

Three decisions that make or break it

It never exits non-zero because an advisory exists. A third party's disclosure timing must not block every pull request in the organisation. A finding opens an issue; the run stays green. It does exit non-zero when the script itself failed — an unreadable pin, a download that did not return, unparseable output. Exercised by breaking a pin name on purpose: exit=1.

Yanked crates get their own section. cargo audit reports them as warnings rather than vulnerabilities, and they matter here for a specific reason measured today: cargo install --locked performs no resolution, so it installs a yanked crate without complaint. The maintainer's "do not use this version" reaches nobody.

The job installs tooling, so it holds no secrets — the tooling-isolation rule applied to the job that enforces the other half of the same issue. Running that check against this repository with the new workflow in place passes.

A limitation of the rule this exposed, worth stating

tooling-isolation checks for secrets.<NAME> references. It does not look at permissions:. This new job carries issues: write and GH_TOKEN: ${{ github.token }} — which the check passes, correctly by its own definition, but that is not zero credential. A build script in the audited tree could open an issue. Low impact: contents: read means it cannot push, and it cannot reach any secret.

The gap worth naming is broader than this job: a workflow with permissions: contents: write and a tooling install would pass the check today while being genuinely dangerous. That is a real hole in a rule I wrote this morning, and it is better recorded than left for someone to discover by trusting the check further than it goes. Not fixed here — widening it needs thought about which permissions are disqualifying and would flag legitimate jobs.

Verification

actionlint clean, ruff check and ruff format --check clean. Run for real against the live RustSec database with only gh stubbed, so crates.io and the advisory data were genuine. Deduplication exercised: with the issue already open it declines to create a second.

The containment half of Glyndor/apt#121 landed earlier: a job that installs
third-party tooling holds no secrets, enforced by workflow-lint. This is the
auditing half. Nothing looked inside those trees, and a version pinned before
an advisory is published stays pinned after it.

The method was proven by hand on 2026-08-24 and is what this automates.
`cargo install --locked` builds the tree in the tool's OWN bundled Cargo.lock,
which ships inside the .crate — not this repository's lockfile. So the audit
downloads the .crate, extracts that lockfile, and runs cargo audit against it.
That is the tree the runner actually builds.

Seven tools, with their versions read from the workflow files rather than
hardcoded, sharing pin-watch's approach so a bump in one place cannot leave
this auditing a version nobody installs.

Yanked crates get their own section. cargo audit reports them as warnings, not
vulnerabilities, and they matter here for a specific reason: `cargo install
--locked` performs no resolution, so it installs a yanked crate without
complaint. The maintainer's "do not use this version" reaches nobody today.

It never exits non-zero because an advisory exists. A third party's disclosure
timing must not block every pull request in the organisation, so a finding
opens an issue and the run stays green. It does exit non-zero when the script
itself failed — an unreadable pin, a download that did not return, output it
could not parse — which was exercised by breaking a pin name deliberately.

Verified against the real advisory database with only `gh` stubbed. The three
hand measurements reproduce: RUSTSEC-2026-0185 in cargo-audit's own tree,
RUSTSEC-2026-0009 in cargo-cyclonedx's, and xml-rs 0.8.19 yanked. It found
five more the hand sweep missed, because that covered three tools and this
covers seven.

The job installs tooling, so it holds no secrets — the tooling-isolation rule
applied to the job that enforces the other half of the same issue.

Refs Glyndor/apt#121

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
@Jaro-c
Jaro-c merged commit 9863522 into main Aug 25, 2026
9 checks passed
@Jaro-c
Jaro-c deleted the ci/121-audit-tooling-trees branch August 25, 2026 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant