Supply-chain audit #34
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
| name: Supply-chain audit | |
| # Daily advisory re-scan, decoupled from PR CI. New RustSec advisories land continuously, so a tree | |
| # that was green at merge time can go vulnerable on a quiet day. This re-runs `cargo deny check | |
| # advisories` against the COMMITTED Cargo.lock using the same deny.toml ignore list as the PR-time | |
| # `cargo-deny-advisories` gate — a newly published advisory that isn't already justified there turns | |
| # this red. `workflow_dispatch` lets you run it on demand. | |
| # | |
| # NOTE: scheduled workflows only fire on the default branch, so this starts running once merged to | |
| # main. Config + the per-advisory justifications live in deny.toml; the remediation backlog is #40. | |
| on: | |
| schedule: | |
| - cron: "0 7 * * *" # 07:00 UTC daily | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| issues: write # so a failed scan can open/refresh a tracking issue (#82) | |
| # One audit at a time: a manual workflow_dispatch overlapping the 07:00 cron must not race the | |
| # search-then-create issue de-dup below into duplicate trackers. (#82) | |
| concurrency: | |
| group: supply-chain-audit | |
| cancel-in-progress: false | |
| jobs: | |
| advisories: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - id: deny | |
| # Pinned to the last-good release: v2.1.0 (2026-07-13) drops a positional arg in action.yml | |
| # without updating its entrypoint, shifting --log-level into a command slot — every run dies | |
| # with `unrecognized subcommand 'warn'`. Unpin to @v2 once upstream ships a fix. | |
| uses: EmbarkStudios/cargo-deny-action@v2.0.20 | |
| with: | |
| command: check advisories | |
| # Make the daily watch LOUD (#82): if the ADVISORY SCAN itself failed (the `steps.deny.outcome` | |
| # guard, so a checkout / action-infra error does NOT masquerade as an advisory), a new RUSTSEC | |
| # advisory or a yanked crate was found — the nightly keeps `yanked = "deny"` even though PRs only | |
| # warn. A red scheduled run just emails admins by default, easy to miss, so open a tracking issue | |
| # (or comment on the open one). `gh` is preinstalled: no third-party action added. | |
| - name: Open or refresh advisory tracking issue on failure | |
| if: failure() && steps.deny.outcome == 'failure' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| title="Daily cargo-deny advisory scan is failing" | |
| run_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" | |
| body=$(printf '%s\n\n%s\n\n%s' \ | |
| "The nightly \`cargo deny check advisories\` run failed — a new RustSec advisory or a yanked dependency was found. See the run for the crate and RUSTSEC id." \ | |
| "Failed run: ${run_url}" \ | |
| "Triage in \`deny.toml\`: bump the dependency, or add a justified \`ignore\` entry. Close this issue once the nightly scan is green again.") | |
| # Tolerate a transient list failure (|| true) so a flaky API can't swallow the alert, and | |
| # match the title EXACTLY (gh search is tokenized/fuzzy) so we never comment on a look-alike. | |
| existing=$(gh issue list -R "$REPO" --state open --search "${title} in:title" \ | |
| --json number,title --jq "map(select(.title == \"${title}\")) | .[0].number // empty" || true) | |
| if [ -n "${existing}" ]; then | |
| gh issue comment "${existing}" -R "$REPO" --body "${body}" | |
| else | |
| gh issue create -R "$REPO" --title "${title}" --body "${body}" | |
| fi |