From e810c7fda5fc8b316c90fea958b1fd32699d127e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 23:54:53 +0000 Subject: [PATCH 1/2] ci: fail on locked dependencies with security advisories Every advisory fixed in #81 to #84 was found by chance, in mix deps.get output that warns but never fails. This workflow makes CI fail instead: - `mix hex.audit` checks mix.lock against Hex's advisory feed (and retired packages). Hex 2.5 is the first release that checks advisories, so the job installs the latest Hex. - `cargo audit` checks Cargo.lock against the RustSec database. cargo-audit takes about three minutes to build, so its binary is cached per version. A lock file can become vulnerable without any change in this repository, so the workflow also runs daily on main. Against the lock files from before those fixes, hex.audit reports six advisories in decimal 3.1.1 and mint 1.9.3, and cargo audit reports RUSTSEC-2026-0285 in rustls 0.23.41. Both exit 1. On current main both pass. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019fnjMN7WpEjQJD2zveakV6 --- .github/workflows/audit.yml | 88 +++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/audit.yml diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..5c8751f --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,88 @@ +--- +name: audit + +# Fails when a locked dependency has a published security advisory. Lock files +# can become vulnerable without any change here, so this also runs daily on +# main instead of only when a pull request touches them. +# +# To acknowledge an advisory that does not affect Textbin, list it under +# `hex: [ignore_advisories: [...]]` in mix.exs, or under +# `[advisories] ignore = [...]` in .cargo/audit.toml, with a comment saying why. + +"on": + pull_request: + push: + branches: [main] + schedule: + - cron: "17 6 * * *" + workflow_dispatch: + +permissions: + contents: read + +env: + OTP_VERSION: "28.5" + ELIXIR_VERSION: "1.20" + CARGO_AUDIT_VERSION: "0.22.2" + +jobs: + hex: + name: Hex advisories + runs-on: blacksmith-2vcpu-ubuntu-2404 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Elixir + uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + + - name: Cache Mix dependencies + uses: actions/cache@v4 + with: + path: deps + key: mix-audit-${{ runner.os }}-${{ hashFiles('mix.lock') }} + restore-keys: | + mix-audit-${{ runner.os }}- + + # Hex 2.5 is the first release whose audit checks security advisories + # as well as retirements; installing the latest keeps that true. + - name: Install Hex + run: mix local.hex --force + + # hex.audit loads the project, which needs the locked deps present. + - name: Install dependencies + run: mix deps.get + + - name: Audit + run: mix hex.audit + + cargo: + name: RustSec advisories + runs-on: blacksmith-2vcpu-ubuntu-2404 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + # Building cargo-audit takes about three minutes; the binary only + # changes with its version. + - name: Cache cargo-audit + id: cargo-audit-cache + uses: actions/cache@v4 + with: + path: ~/.cargo/bin/cargo-audit + key: cargo-audit-${{ runner.os }}-${{ env.CARGO_AUDIT_VERSION }} + + - name: Install cargo-audit + if: steps.cargo-audit-cache.outputs.cache-hit != 'true' + run: cargo install cargo-audit --locked --version "$CARGO_AUDIT_VERSION" + + - name: Audit + run: cargo audit From 24bb39b3fbac872350203777de9191761b5ff280 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 23:54:53 +0000 Subject: [PATCH 2/2] docs(agents): explain the dependency advisory check Contributors and agents should know that an advisory fails CI, that the fix is an upgrade, and where an advisory that does not affect Textbin can be acknowledged, with a reason. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019fnjMN7WpEjQJD2zveakV6 --- AGENTS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AGENTS.md b/AGENTS.md index 595db0b..7f354d3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,6 +8,7 @@ This is a web application written using the Phoenix web framework. - When Jujutsu is available for the workspace, use `jj` to record changes with detailed explanations; use Git commits only when `jj` is unavailable - Add concise comments or module/function docs that explain intent, domain boundaries, security decisions, or non-obvious tradeoffs. Avoid comments that merely restate what the code does - The API contract lives next to the code: each API controller action declares its OpenAPI operation with `operation` (helpers in `TextbinWeb.ApiDocs`, schemas in `TextbinWeb.ApiSpec.Schemas`). Requests that do not match it are rejected, every API response in the test suite is checked against it, and a full `mix test` fails if a documented response is never produced. After changing an operation, run `mix openapi.export` and commit `priv/openapi.json` +- CI fails when a locked dependency has a published security advisory (`mix hex.audit` and `cargo audit`, on every pull request and daily). Fix it by upgrading; acknowledge an advisory that does not affect Textbin only in `hex: [ignore_advisories: ...]` in `mix.exs` or `.cargo/audit.toml`, with a comment explaining why - Use the already included and available `:req` (`Req`) library for HTTP requests, **avoid** `:httpoison`, `:tesla`, and `:httpc`. Req is included by default and is the preferred HTTP client for Phoenix apps ### Phoenix v1.8 guidelines