From 38051680974844c20db3f83742947a39f8487af3 Mon Sep 17 00:00:00 2001 From: Jim Huang Date: Mon, 26 Oct 2026 03:42:37 +0800 Subject: [PATCH 1/2] Bump rustls-webpki and webpki-root-certs Clears RUSTSEC-2026-0382 and RUSTSEC-2026-0383, which failed the cargo audit job. --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0691fda..0f62dc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1702,9 +1702,9 @@ checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" [[package]] name = "rustls-webpki" -version = "0.103.15" +version = "0.103.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281" dependencies = [ "aws-lc-rs", "ring", @@ -1985,7 +1985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.3", + "getrandom 0.3.4", "once_cell", "rustix", "windows-sys 0.61.2", @@ -2471,9 +2471,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +checksum = "e49d0e65abc00c142e8f9428da991da9f1a250c39be97459901eb12b9b49f62a" dependencies = [ "rustls-pki-types", ] From d0f8e5af8d35d09b18d71a22d94237a753f33cdd Mon Sep 17 00:00:00 2001 From: Jim Huang Date: Mon, 26 Oct 2026 03:42:37 +0800 Subject: [PATCH 2/2] Audit advisories daily and lint workflows A weekly cron in ci.yml re-ran every job to reach the audit, so an advisory could wait days. security.yml now runs cargo audit daily and on demand, and the cron is gone. The lint job runs zizmor over the workflows, checkouts no longer persist credentials, and Dependabot waits seven days before proposing a release. --- .github/dependabot.yml | 8 ++++++++ .github/workflows/ci.yml | 29 +++++++++++++++++------------ .github/workflows/security.yml | 26 ++++++++++++++++++++++++++ docs/development.md | 12 +++++++----- 4 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/security.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ec5add3..0047f53 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,6 +6,10 @@ updates: directory: / schedule: interval: weekly + # A release has a week to be yanked or flagged before it is proposed. + # Security updates ignore the cooldown. + cooldown: + default-days: 7 groups: actions: patterns: ["*"] @@ -18,6 +22,10 @@ updates: directory: / schedule: interval: weekly + # A release has a week to be yanked or flagged before it is proposed. + # Security updates ignore the cooldown. + cooldown: + default-days: 7 groups: cargo: update-types: [minor, patch] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4fc60f..83307ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,10 +4,6 @@ on: push: branches: [main] pull_request: - # Advisories land without a code change; surface them on main rather than - # on whichever unrelated pull request happens to run next. - schedule: - - cron: "0 6 * * 1" permissions: contents: read @@ -15,10 +11,9 @@ permissions: # A newer run cancels an older one, except on main, where a run may be holding # the release window below open with the previous release already deleted, and # cancelling it would leave no release at all. A newer pending run still replaces an older pending one, so -# only the newest push waiting on main survives. The event is in the key so -# the weekly scheduled run, which never releases, cannot be what replaces it. +# only the newest push waiting on main survives. concurrency: - group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} # Every action is pinned to a commit, with its release in a comment for @@ -45,6 +40,8 @@ jobs: timeout-minutes: 30 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1 with: toolchain: ${{ env.PINNED_RUST }} @@ -72,11 +69,19 @@ jobs: timeout-minutes: 15 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1 with: toolchain: ${{ env.PINNED_RUST }} components: clippy, rustfmt - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3 + # Workflow security audit (unpinned actions, excess permissions, template + # injection). Offline because the online audits need a token. + - uses: taiki-e/install-action@a4d3f3f761dcd95cce1b72cb61cb6d1963438177 # v2.87.30 + with: + tool: zizmor@1.31.1 + - run: zizmor --offline .github - run: cargo fmt --check - run: python3 tests/coverage.py - uses: taiki-e/install-action@a4d3f3f761dcd95cce1b72cb61cb6d1963438177 # v2.87.30 @@ -91,13 +96,13 @@ jobs: coverage: needs: test - if: github.event_name != 'schedule' runs-on: ubuntu-24.04 timeout-minutes: 5 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 + persist-credentials: false - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: pattern: coverage-* @@ -145,6 +150,8 @@ jobs: timeout-minutes: 15 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1 with: toolchain: 1.88.0 @@ -156,6 +163,8 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false # cargo audit itself rather than rustsec/audit-check, whose releases # still run on Node.js 20. Pinned like the lint toolchain. - uses: taiki-e/install-action@a4d3f3f761dcd95cce1b72cb61cb6d1963438177 # v2.87.30 @@ -168,12 +177,8 @@ jobs: # Prebuilt binaries, one per platform the test matrix above runs on. A binary # for a platform whose suite never ran there is a promise nobody checked, so # widen that matrix before adding a row here. - # - # Not on the weekly schedule: that run exists for the audit and never - # releases, so binaries built there would go nowhere. build: name: Binary ${{ matrix.target }} - if: github.event_name != 'schedule' strategy: fail-fast: false matrix: diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..31bc73f --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,26 @@ +name: Security + +# Advisories land without a code change, so this runs on a clock rather than +# on pushes. ci.yml still audits every push and pull request, and gates the +# release on it; this is the run that notices a new advisory between them. +on: + schedule: + - cron: "17 6 * * *" + workflow_dispatch: + +permissions: + contents: read + +jobs: + audit: + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: taiki-e/install-action@a4d3f3f761dcd95cce1b72cb61cb6d1963438177 # v2.87.30 + with: + tool: cargo-audit@0.22.2 + # Same flags as ci.yml: unsound and yanked crates fail like vulnerabilities. + - run: cargo audit --deny warnings diff --git a/docs/development.md b/docs/development.md index 92afff3..6218e7f 100644 --- a/docs/development.md +++ b/docs/development.md @@ -152,19 +152,21 @@ TTL alone never supplies content for conflict detection. ## Continuous integration -`.github/workflows/ci.yml` runs on pushes to `main`, on pull requests, and -weekly: +`.github/workflows/ci.yml` runs on pushes to `main` and on pull requests: | Job | What it checks | |-----|----------------| | `test` | the full suite with line coverage on Linux, macOS, and Windows | -| `lint` | formatting, coverage-script tests, unused dependencies, duplicate reporting, and clippy on a pinned toolchain | -| `coverage` | merged platform reports and the changed-line coverage ratchet; skipped on the weekly run | +| `lint` | formatting, workflow security (zizmor), coverage-script tests, unused dependencies, duplicate reporting, and clippy on a pinned toolchain | +| `coverage` | merged platform reports and the changed-line coverage ratchet | | `msrv` | the build on the `rust-version` in `Cargo.toml` | | `audit` | RustSec advisories against `Cargo.lock` | -| `build` | release binaries for each tested platform, Linux against glibc 2.17; skipped on the weekly run | +| `build` | release binaries for each tested platform, Linux against glibc 2.17 | | `release` | on pushes to `main` only, after every job above passes, replaces the `latest` GitHub release | +`.github/workflows/security.yml` runs the same `cargo audit` daily and on demand, +because advisories land without a code change; Dependabot proposes the fix. + The release scripts live in `.ci/`; their header comments explain the ordering that keeps a failed run from leaving no release behind.