From 94c7b962c26101257b3831ee4ed186484bea5769 Mon Sep 17 00:00:00 2001 From: Pushkinist <4850452+Pushkinist@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:37:05 +0700 Subject: [PATCH] chore(security): pin CI actions to SHAs, add cosign release signing, drop stale advisory ignore Pin actions/checkout, dtolnay/rust-toolchain, Swatinem/rust-cache to commit SHAs (version comments kept for Dependabot) to close the mutable-tag vector. Add 'make release-sign' (keyless cosign sign-blob -> .cosign.bundle) plus RELEASING docs + consumer verify, giving the locally-built prebuilt binary real provenance. Remove the now-unmatched RUSTSEC-2025-0119 ignore (tokenizers 0.23 dropped the number_prefix chain). --- .github/workflows/ci.yml | 10 +++---- Makefile | 5 +++- deny.toml | 2 -- docs/RELEASING.md | 27 +++++++++++++++-- scripts/release/sign_artifact.sh | 50 ++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 10 deletions(-) create mode 100755 scripts/release/sign_artifact.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b932077..9ca7900 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,8 +19,8 @@ jobs: name: rustfmt runs-on: macos-latest steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@stable + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable with: components: rustfmt - run: cargo fmt --all -- --check @@ -29,15 +29,15 @@ jobs: name: build + clippy runs-on: macos-14 # Apple Silicon steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@stable + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable with: components: clippy - name: Install MLX (provides mlx-c) run: brew install mlx-c - name: Resolve MLX_C_PREFIX run: echo "MLX_C_PREFIX=$(brew --prefix mlx-c)" >> "$GITHUB_ENV" - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2 - name: clippy run: cargo clippy --workspace --all-targets --all-features -- -D warnings - name: build diff --git a/Makefile b/Makefile index d911e87..e823e4e 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ PROF_GEN ?= 500 AUDIT_IGNORES := --ignore RUSTSEC-2024-0436 --ignore RUSTSEC-2025-0119 .PHONY: help build check test fmt fmt-check lint audit deny precommit hooks \ - ci ci-metrics tag release-package release-sha tap-sync \ + ci ci-metrics tag release-package release-sha release-sign tap-sync \ clean serve chat info logs-tail metrics-summary \ metrics-init metrics-doctor metrics-doctor-fix metrics-export \ metrics-backup metrics-replay-pending metrics-prompts-sync \ @@ -178,6 +178,9 @@ release-package: ## build + bundle dist/rmlx-v-aarch64-apple-darwin.tar.gz release-sha: ## print sha256 of the v GitHub source tarball (append --write to patch the formula) bash scripts/release/source_sha256.sh +release-sign: ## keyless cosign-sign dist/rmlx-v-...tar.gz -> .cosign.bundle (needs cosign + browser OIDC) + bash scripts/release/sign_artifact.sh + tap-sync: ## copy packaging/homebrew/rmlx.rb into the homebrew-rmlx tap and push bash scripts/release/sync_tap.sh diff --git a/deny.toml b/deny.toml index 84994a3..c3376b3 100644 --- a/deny.toml +++ b/deny.toml @@ -21,8 +21,6 @@ yanked = "deny" ignore = [ # `paste` archived. Transitive via `tokenizers`. "RUSTSEC-2024-0436", - # `number_prefix` unmaintained. Transitive via `indicatif` -> `tokenizers`. - "RUSTSEC-2025-0119", ] # ---- licenses ----------------------------------------------------------- diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 733ad6b..c781925 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -38,7 +38,19 @@ crates are `publish = false`). There is no separate `VERSION` file. dist/rmlx-v-aarch64-apple-darwin.tar.gz \ dist/rmlx-v-aarch64-apple-darwin.tar.gz.sha256 ``` -7. **Formula url + sha256:** bump the `url` line in +7. **Sign the artifact (keyless cosign):** `make release-sign` → + `dist/rmlx-v-aarch64-apple-darwin.tar.gz.cosign.bundle` (needs + `brew install cosign`; opens a browser OIDC prompt). Upload it: + ```sh + gh release upload v \ + dist/rmlx-v-aarch64-apple-darwin.tar.gz.cosign.bundle + ``` + The release binary is built locally (hosted CI has no Metal), so the + `.sha256` alone is self-attested. The cosign bundle binds the tarball to + your authenticated identity + the public Rekor log — real provenance for + the prebuilt binary. Consumer-side verification is in "Verify both install + paths". +8. **Formula url + sha256:** bump the `url` line in `packaging/homebrew/rmlx.rb` to the new `v` tag tarball, **then** `make release-sha` (or `bash scripts/release/source_sha256.sh --write`) for the sha256. @@ -47,7 +59,7 @@ crates are `publish = false`). There is no separate `VERSION` file. > sha against the old tag's url and `brew install` fails with a sha > mismatch. Always edit the `url` line yourself. Commit the formula bump via a PR (`main` is ruleset-protected; see below). -8. **Publish the tap:** `make tap-sync` (copies the formula into +9. **Publish the tap:** `make tap-sync` (copies the formula into `Pushkinist/homebrew-rmlx` as `Formula/rmlx.rb` and pushes). ## Dependency-bump PRs (Dependabot) @@ -84,6 +96,17 @@ tar xzf rmlx-v-aarch64-apple-darwin.tar.gz ./rmlx-v-aarch64-apple-darwin/rmlx --version ``` +**Provenance (cosign bundle, if the release ships one):** +```sh +gh release download v -p '*.cosign.bundle' +cosign verify-blob \ + --bundle rmlx-v-aarch64-apple-darwin.tar.gz.cosign.bundle \ + --certificate-identity \ + --certificate-oidc-issuer \ + rmlx-v-aarch64-apple-darwin.tar.gz +# issuer: GitHub https://github.com/login/oauth · Google https://accounts.google.com +``` + **Homebrew (build from source):** ```sh brew tap Pushkinist/rmlx diff --git a/scripts/release/sign_artifact.sh b/scripts/release/sign_artifact.sh new file mode 100755 index 0000000..c9c1091 --- /dev/null +++ b/scripts/release/sign_artifact.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# sign_artifact.sh — keyless (sigstore) signature for the release tarball. +# +# Produces, alongside dist/rmlx-v-aarch64-apple-darwin.tar.gz: +# .cosign.bundle self-contained cert + signature + Rekor proof +# +# Keyless signing: cosign opens a browser for an OIDC login (GitHub / Google), +# obtains a short-lived Fulcio certificate bound to that identity, signs the +# tarball, and records the signature in the public Rekor transparency log. There +# is no private key to store or leak. Upload the .cosign.bundle as a release +# asset; consumers verify with the command this script prints (also documented +# in docs/RELEASING.md). +# +# The release binary is built locally (hosted CI has no Metal — see RELEASING), +# so this is the provenance signal the prebuilt tarball would otherwise lack: +# the bundle binds the artifact to the maintainer's authenticated identity. +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +cd "$REPO_ROOT" + +command -v cosign >/dev/null 2>&1 || { + echo "error: cosign not found. Install it first: brew install cosign" >&2 + exit 1 +} + +VER=$(awk -F'"' '/^version = /{print $2; exit}' Cargo.toml) +[ -n "$VER" ] || { echo "error: could not read version from Cargo.toml" >&2; exit 1; } + +TARBALL="dist/rmlx-v${VER}-aarch64-apple-darwin.tar.gz" +[ -f "$TARBALL" ] || { + echo "error: $TARBALL not found — run 'make release-package' first" >&2 + exit 1 +} + +BUNDLE="${TARBALL}.cosign.bundle" +echo "==> keyless-signing ${TARBALL} (a browser OIDC prompt will open)" +cosign sign-blob --yes --bundle "$BUNDLE" "$TARBALL" +echo "==> wrote ${BUNDLE}" +echo +echo "Upload it to the release:" +echo " gh release upload v${VER} ${BUNDLE}" +echo +echo "Verify (substitute the identity you authenticated as):" +echo " cosign verify-blob \\" +echo " --bundle ${BUNDLE} \\" +echo " --certificate-identity \\" +echo " --certificate-oidc-issuer \\" +echo " ${TARBALL}" +echo " (GitHub issuer: https://github.com/login/oauth · Google: https://accounts.google.com)"