feat: rust-packages pipeline and supply-chain hardening#25
Open
ob-aion wants to merge 24 commits into
Open
Conversation
- rust/base composite: resolve the toolchain from rust-toolchain.toml, cache cargo + target, run an optional ci/setup.sh native-dep hook, then fmt --check, clippy -D warnings, test — zero-input, mirroring javascript/base - rust-packages.yml reusable workflow: preflight matrix (ubuntu/macos/windows), cargo-deny when deny.toml is present, tag-driven publish (pin Cargo.toml to the tag, generate-changelog, cargo publish to crates.io, github-release, commit-back, rolling vN tag), security — reuses the transverse release and security actions - README: document the pipeline, the rust/base action, and the ci/setup.sh convention
…y-chain job - drop the supply-chain (cargo-deny) job: the npm pipeline has no counterpart (preflight/publish/security only), it partly overlapped security.yml's osv-scanner, and it recompiled cargo-deny on every push — consumers add their own when needed - rust/base: cache only the ~/.cargo deps (drop target, a stale-build risk), terse one-line description matching javascript/base, drop the verbose hook comment - publish: cargo publish --no-verify (the tag is main HEAD, already built and tested by preflight) — removes the duplicated ci/setup.sh hook and the cold build on release - header, name, and permission-comment wording aligned with javascript-npm-packages; README drops the cargo-dist aside and documents the CARGO_REGISTRY_TOKEN secret
I was wrong to drop cargo-deny as "redundant": osv-scanner only scans for known vulnerabilities. cargo-deny owns the controls nothing else here covers — crate sources (crates.io only), licenses, banned/wildcard deps, and unmaintained or yanked advisories. Restored, mapped to the GitLab image-layer hardening model. - supply-chain job: cargo-deny check via the SHA-pinned EmbarkStudios action, on every branch push, reading the repo's deny.toml - rust/base: clippy and test run --locked (committed Cargo.lock, fails on drift) — the lock-pin control; deny.toml + committed Cargo.lock are now consumer requirements - README Security: a "Supply chain — Rust" section mapping each GitLab control (cooldown, firewall, no-scripts, pin) to its Rust analog, the baseline deny.toml, and two accepted residual risks (build.rs runs; crates.io has no publish cooldown)
…+ policy - javascript/base: install Socket Firewall (sfw, the free tokenless build) and run the install through it (sfw pnpm install) — blocks confirmed-malicious packages at the registry fetch, the GitHub-runner equivalent of the GitLab image-baked firewall; fail-closed if sfw cannot install or run - README Security: Firewall and Cooldown sections; the cooldown (minimumReleaseAge, @coroboros/* excluded) belongs in pnpm-workspace.yaml on pnpm 11, not .npmrc - SECURITY.md: vulnerability-reporting policy, matching the GitLab repo
Reusable workflows run in the caller's context, so "Move rolling major tag" force-pushed a meaningless vN ref into every consumer package repo. Packages release x.y.z; the @vn ref to coroboros/ci belongs to its own release path. This matches the GitLab model — ADR-0005 keeps rolling tags image-only and gives packages no rolling ref. Commit-back was duplicated inline across the npm and rust publish jobs. Extract it into release/commit-artifacts (files input), mirroring GitLab's commit-release-artifacts template, with [skip ci] so the bot's main push no longer re-triggers the pipeline.
Each scanner (gitleaks, osv-scanner, cargo-deny) becomes a security/* composite, so osv-scanner is defined once and reused by both security.yml and the package supply-chain gates. osv-scanner now runs only when a supported manifest is present, so a dependency-less repo wiring in security.yml skips the scan instead of failing on osv's no-manifest error. self-security.yml references the composites via local ./ refs: a reusable workflow resolves ./ against the caller, so security.yml must pin @v0 while self-CI exercises the in-repo composites.
crates.io Trusted Publishing (OIDC) via rust-lang/crates-io-auth-action is the default; CARGO_REGISTRY_TOKEN is the bootstrap for a new crate's first publish. Drop --no-verify so the verify build compiles the packaged tarball and catches a crate that only builds in-workspace before an immutable release. publish needs the supply-chain job, so cargo-deny gates the release rather than scanning in parallel. The ci/setup.sh hook moves to rust/native-deps, shared by rust/base and the publish verify build.
osv-scanner ran only in the parallel security job, so a known vulnerability could not block a release. A supply-chain job now runs it on every push and publish needs it.
The previous mover ran inside the reusable publish job, in the consumer's context, and force-pushed a meaningless vN ref into every package repo. Moving v0 belongs to coroboros/ci's release path; self-release.yml does it on each stable X.Y.Z tag.
Document the Rust pipeline, the supply-chain gates, the security composites, the manifest-gated osv scan, and the v0 automation. Apply the Coroboros brand voice to the README prose. Bump package.json to 0.2.0, resolving the lag behind the 0.1.14 tag, and prepend the CHANGELOG section.
osv-scanner can't parse the binary bun.lockb (only the text bun.lock), so a bun.lockb-only repo tripped the gate: detect fires, osv resolves nothing, exits 128, the job fails spuriously — the exact case the manifest gate exists to avoid. Add pdm.lock (PDM, osv-supported) so the set matches coroboros/security/ci one-to-one (13 lockfiles).
Add a secrets job (gitleaks composite, full history) to the npm and Rust package workflows; publish now needs [supply-chain, secrets]. A leaked secret blocks the release through the template's needs: graph, not the consumer's branch protection — parity with the GitLab security-gate stage.
Symmetry with the gitleaks row — osv-scanner also runs in self-security.yml.
Opt-in via [package.metadata.dist] in the consumer's Cargo.toml. A tagged binary repo gets prebuilt archives per target, shell/powershell installers, a Homebrew formula in the declared tap, and an npm shim — attached to the one GitHub Release the pipeline already creates, alongside the crates.io publish. The shared pipeline stays the sole release authority: dist only builds (final asset URLs derive from repo + tag), and the release goes live via draft → undraft so installers and the formula resolve against published URLs. Library crates without dist metadata are unchanged — every binary job self-skips. Adds dist-plan/build/host/publish (binary builds need the cargo-deny and gitleaks gates), the release/dist install composite (dist 0.32.0, cargo install --locked), and a draft input on release/github-release. Bumps to 0.3.0.
The cargo set-version tag pin was inlined in four jobs — publish plus the three dist jobs. Extract it to rust/pin-version, called from each: one source for the pin, consistent with the repo's composite-per-shared-step shape. Docs: tighten the README binary-distribution section, cut the duplicated artifact list (the details block is the one home), drop a stale needs: list and a misplaced cargo-dist pinning note, and fix the cfg(target_os) example. Record release/dist + rust/pin-version in CLAUDE.md and the changelog.
House style pins every tooling binary by version — gitleaks, actionlint, cargo-dist. cargo-edit was the last unpinned one. 0.13.11 via env, matching the release/dist install.
The supply-chain gate read the consumer's deny.toml, so a repo could weaken or omit it. Ship a canonical security/deny.toml and pass it via --config (the gitleaks model): the consumer's deny.toml is ignored, and a project-local deny.exceptions.toml — cargo-deny's one remaining license escape hatch — fails the job. The ruleset hard-fails on vulnerability, yanked, unmaintained, and unsound advisories, restricts sources to crates.io (git + alternative registries denied), and denies wildcard version requirements. Validated against cargo-deny 0.19.8.
The supply-chain job blurb still said cargo-deny "reads deny.toml"; the risk table omitted unsound; the Security prose re-listed the controls the table already covers. Point both at the imposed security/deny.toml and drop the duplication.
Remove workflow/composite comments that restate what the code does or duplicate the README: the cargo-deny / gitleaks / osv job-purpose blocks (and a stale "reads the repo's deny.toml" line), the packages_install and npm-auth notes. Trim the verbose ones to their why — the Socket Firewall, verify-build, undraft-ordering, and install_dist comments. Inline scope justifications, the pnpm/corepack workaround, and the URL-determinism rationale stay.
Inline the dist setup (mimic the npm pipeline) — drop the release/dist and rust/pin-version composites; the `cargo install cargo-dist` / `cargo-edit` + `cargo set-version` steps live in the dist jobs and publish, with tool versions hoisted to workflow env (CARGO_DIST_VERSION, CARGO_EDIT_VERSION). Logs use gh-native commands per context: verify-tag detail folded into one `::error::`, status lines to `::notice::`, the check-docs context dump into a `::group::`. Trim the gitleaks and self-security comments to one line.
0.2.0 was never tagged — the latest release is 0.1.14 — so the whole rust-packages PR ships as one version. Revert the second bump and merge the binary-distribution and imposed-cargo-deny notes into the v0.2.0 changelog.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
rust-packages.yml,rust/base,rust/native-deps) — preflight matrix (Linux, macOS, Windows), acargo-denysupply-chain job, tag-driven publish, and the shared security scan.rust-lang/crates-io-auth-action);CARGO_REGISTRY_TOKENbootstraps a new crate's first publish. The verify build runs (no--no-verify), catching a crate that only builds in-workspace.needs:—cargo-denyfor Rust,osv-scannerfor npm — so a release is blocked on a known issue. The parallelsecurityscan is reporting only, not the gate.gitleaks,osv-scanner, andcargo-denyextracted tosecurity/*as the single source, reused by the gates. osv runs only when a supported manifest is present, so a code-less repo skips it instead of failing on osv's no-manifest error.v0automation (self-release.yml) moves the rolling tag on each stable release; the consumer-context rolling-tag push is removed. Socket Firewall added tojavascript/base. README brand-voice pass. Version0.2.0.Test plan
actionlint -shellcheck=shellcheckandyamllint -c .yamllint .exit 0 (verified locally)@v0after release; first tag bootstraps withCARGO_REGISTRY_TOKEN, then crates.io Trusted Publishing — setup in READMEpnpm-lock.yamlfailssupply-chainand blocks the tagged publishX.Y.Zoncoroboros/cimovesv0(needsGITHUB_TOKENtag-push allowance if av*ruleset protects it)