Skip to content

feat: rust-packages pipeline and supply-chain hardening#25

Open
ob-aion wants to merge 24 commits into
mainfrom
feat/rust-packages
Open

feat: rust-packages pipeline and supply-chain hardening#25
ob-aion wants to merge 24 commits into
mainfrom
feat/rust-packages

Conversation

@ob-aion
Copy link
Copy Markdown
Contributor

@ob-aion ob-aion commented Jun 2, 2026

Summary

  • Rust pipeline (rust-packages.yml, rust/base, rust/native-deps) — preflight matrix (Linux, macOS, Windows), a cargo-deny supply-chain job, tag-driven publish, and the shared security scan.
  • Publish authenticates with crates.io OIDC by default (rust-lang/crates-io-auth-action); CARGO_REGISTRY_TOKEN bootstraps a new crate's first publish. The verify build runs (no --no-verify), catching a crate that only builds in-workspace.
  • Both publish jobs gate on supply chain via needs:cargo-deny for Rust, osv-scanner for npm — so a release is blocked on a known issue. The parallel security scan is reporting only, not the gate.
  • Security compositesgitleaks, osv-scanner, and cargo-deny extracted to security/* 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.
  • v0 automation (self-release.yml) moves the rolling tag on each stable release; the consumer-context rolling-tag push is removed. Socket Firewall added to javascript/base. README brand-voice pass. Version 0.2.0.

Test plan

  • actionlint -shellcheck=shellcheck and yamllint -c .yamllint . exit 0 (verified locally)
  • osv composite skips a lockfile-less repo and scans when a lock is present (verified locally against this repo)
  • Rust consumer wires @v0 after release; first tag bootstraps with CARGO_REGISTRY_TOKEN, then crates.io Trusted Publishing — setup in README
  • npm consumer: a high-severity CVE in pnpm-lock.yaml fails supply-chain and blocks the tagged publish
  • Tagging a stable X.Y.Z on coroboros/ci moves v0 (needs GITHUB_TOKEN tag-push allowance if a v* ruleset protects it)

ob-aion added 4 commits June 2, 2026 13:53
- 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
@ob-aion ob-aion changed the title feat: add rust-packages pipeline and rust/base action feat: rust-packages pipeline and supply-chain hardening Jun 2, 2026
ob-aion added 20 commits June 2, 2026 14:54
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant