From 8a7359d27cd3a5b2dbb5cba46ad590f3920981de Mon Sep 17 00:00:00 2001 From: Craig Constable Date: Wed, 23 Sep 2026 14:47:59 +1000 Subject: [PATCH] chore(security): add dependency scanning and update automation - Add Dependabot and cargo audit/cargo-deny checks - Gate releases on locked dependency validation - Document security policies, exceptions, and local checks --- .github/dependabot.yml | 26 +++++++++ .github/workflows/dependency-security.yml | 42 ++++++++++++++ .github/workflows/release.yml | 8 ++- README.md | 3 + deny.toml | 49 ++++++++++++++++ docs/dependency-security.md | 68 +++++++++++++++++++++++ 6 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dependency-security.yml create mode 100644 deny.toml create mode 100644 docs/dependency-security.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..5132c7bf --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,26 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 10 + groups: + # Keep the closely coupled GUI crates together. The local egui-winit + # patch still needs manual review; see docs/dependency-security.md. + egui: + patterns: + - eframe + - egui* + - epaint* + - emath + - ecolor + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + groups: + github-actions: + patterns: + - "*" diff --git a/.github/workflows/dependency-security.yml b/.github/workflows/dependency-security.yml new file mode 100644 index 00000000..33539a47 --- /dev/null +++ b/.github/workflows/dependency-security.yml @@ -0,0 +1,42 @@ +name: Dependency security + +on: + pull_request: + push: + branches: ["**"] + schedule: + # Find newly published advisories even when the lockfile has not changed. + - cron: "23 3 * * *" + workflow_dispatch: + workflow_call: + +permissions: + contents: read + +jobs: + dependencies: + name: ${{ matrix.tool }} + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - tool: cargo-audit + version: 0.22.2 + command: cargo audit --file Cargo.lock + - tool: cargo-deny + version: 0.20.2 + command: cargo deny --locked check + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false + + - uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable + + - name: Install ${{ matrix.tool }} + run: cargo install --locked ${{ matrix.tool }} --version ${{ matrix.version }} + + - name: Check dependencies + run: ${{ matrix.command }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ec84f999..2d9215eb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,13 @@ env: WINGET_PACKAGE_ID: CodeZeno.ClaudeCodeUsageMonitor jobs: + dependency-security: + uses: ./.github/workflows/dependency-security.yml + permissions: + contents: read + build: + needs: dependency-security runs-on: windows-latest steps: - uses: actions/checkout@v4 @@ -26,7 +32,7 @@ jobs: run: cargo test --locked updater - name: Build release binary - run: cargo build --release + run: cargo build --release --locked - name: Create GitHub Release uses: softprops/action-gh-release@v2 diff --git a/README.md b/README.md index 66f1247c..9fb3a663 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,9 @@ cargo build --release The executable will be created at `target\release\claude-code-usage-monitor.exe`. +See [dependency security](docs/dependency-security.md) for automated dependency +updates, CI security checks, and the commands to run those checks locally. + ## License Licensed under the [MIT License](LICENSE). diff --git a/deny.toml b/deny.toml new file mode 100644 index 00000000..a2e5ae84 --- /dev/null +++ b/deny.toml @@ -0,0 +1,49 @@ +# Policy for the shipped Windows application, including build/dev dependencies. +# cargo-audit separately checks every package in Cargo.lock on all platforms. +[graph] +targets = ["x86_64-pc-windows-msvc"] + +[advisories] +yanked = "deny" +unmaintained = "all" +unsound = "all" +unused-ignored-advisory = "deny" +# Owner: repository maintainers. Review by 2026-10-23, or on an oxifont update. +# Build-time only: oxifont processes font bytes bundled by locked dependencies. +# No patched release exists; migrate/update the subsetter to remove this crate. +# Keep cargo-audit's warning visible. This does not ignore future vulnerabilities. +ignore = [ + { id = "RUSTSEC-2026-0192", reason = "ttf-parser 0.25.1 is unmaintained; required by oxifont-subset/oxifont-parser 0.2.2 for build-time bundled font subsetting. Owner: repository maintainers; review by 2026-10-23. See docs/dependency-security.md." }, +] + +[licenses] +confidence-threshold = 0.93 +allow = [ + "Apache-2.0", + "BSL-1.0", + "CDLA-Permissive-2.0", + "ISC", + "MIT", + "Unicode-3.0", + "Zlib", +] + +# Limit the existing font and file-level copyleft licenses to these crates. +[[licenses.exceptions]] +name = "epaint_default_fonts" +allow = ["OFL-1.1", "Ubuntu-font-1.0"] + +[[licenses.exceptions]] +name = "option-ext" +allow = ["MPL-2.0"] + +[bans] +# The GUI/Windows dependency graph currently needs multiple crate versions. +multiple-versions = "warn" +wildcards = "deny" + +[sources] +unknown-registry = "deny" +unknown-git = "deny" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +allow-git = [] diff --git a/docs/dependency-security.md b/docs/dependency-security.md new file mode 100644 index 00000000..69f48f72 --- /dev/null +++ b/docs/dependency-security.md @@ -0,0 +1,68 @@ +# Dependency security + +Dependabot checks Cargo dependencies and GitHub Actions weekly and opens update +pull requests. Review the manifest and lockfile diff and run the Windows tests +before merging. The egui family is grouped because its versions are coupled. + +The `Dependency security` workflow runs on pull requests, branch pushes, a daily +schedule, and manual dispatch. The release workflow calls the same checks and +waits for them before building or publishing. Checks use read-only repository +permissions and do not require secrets. + +- `cargo audit` checks the entire committed `Cargo.lock` against the current + RustSec advisory database and fails on known vulnerabilities. Informational + warnings remain visible in its output. +- `cargo deny --locked check` checks advisories, licenses, bans, and sources for + `x86_64-pc-windows-msvc`, including build and development dependencies. Known + vulnerabilities, unsound or unmaintained dependencies, yanked versions, + unapproved licenses, wildcard requirements, and unapproved registry/Git + sources fail the check. Multiple crate versions produce warnings. + +The scanners fetch current advisory data on each run. Fix a finding by updating +or replacing the dependency where possible. Any necessary exception must be +narrow and document the advisory, applicability, owner, and review date; do not +disable a category of checks to make CI pass. License exceptions in `deny.toml` +are limited to the existing bundled fonts and `option-ext`. + +### Current advisory exception + +[`RUSTSEC-2026-0192`](https://rustsec.org/advisories/RUSTSEC-2026-0192.html) +reports that `ttf-parser` is unmaintained, with no patched release. Version +0.25.1 is pulled in by `oxifont-subset`/`oxifont-parser` 0.2.2, the latest +available releases when this policy was added. This is a build dependency: +`build.rs` subsets the Ubuntu and Lucide font bytes bundled by locked crates; +it does not parse user-provided fonts at runtime through this dependency. + +`deny.toml` temporarily excepts only this advisory. `cargo audit` still displays +its warning, and future vulnerability advisories remain blocking. The repository +maintainers own reviewing this exception by **2026-10-23**, or sooner when +oxifont updates. Update or replace the subsetter to remove `ttf-parser`, then +remove the exception; cargo-deny rejects unused advisory exceptions. The review +date is a maintenance reminder, not an automatically enforced expiration. + +## Run locally + +Use the same scanner versions as `.github/workflows/dependency-security.yml`: + +```powershell +cargo install --locked cargo-audit --version 0.22.2 +cargo install --locked cargo-deny --version 0.20.2 +cargo audit --file Cargo.lock +cargo deny --locked check +``` + +Scanner versions are pinned in the workflow and must be updated there and in +these commands together; Dependabot's Cargo updates cover application manifests, +not `cargo install` commands. The security workflow's GitHub Action revisions +are pinned to commits and covered by the GitHub Actions updater. Require both +scanner checks in branch protection if merges must be blocked; that repository +setting is separate from the workflow files. + +## Vendored dependency + +`vendor/egui-winit` is a local patch and is not automatically refreshed by +Dependabot. When the egui/eframe family changes, follow +[`vendor/egui-winit/PATCH.md`](../vendor/egui-winit/PATCH.md) to update the matching +upstream source, reapply the text-only clipboard changes, and verify the feature +graph. Review upstream security fixes for this copy explicitly: registry +advisories and source allowlists do not validate locally modified source code.