Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,37 @@ jobs:
# consumer's feature unification can reach. This step is what fails the
# build on a vulnerability in that pruned remainder.
#
# Informational (unsound / unmaintained) advisories are reported here,
# not enforced — cargo-audit exits 0 on them. Enforcing would mean any
# newly published advisory against any transitive crate blocks every
# open PR regardless of its diff; that treadmill is a known cost in
# this org. Vulnerabilities do fail, which is the coverage deny lacks.
# Advisory posture (LAB-1152): vulnerabilities fail every run; yanked
# fails only the weekly schedule run — a yank lands against an
# unchanged lockfile and must not block unrelated PRs, and the
# schedule run exists precisely for advisories no PR diff touches.
# unsound / unmaintained warn on every event. Decision of record with
# the full per-class rationale: deny.toml [advisories].
#
# Runs even when the step above failed so one run shows both verdicts.
if: ${{ !cancelled() }}
run: cargo audit
run: |
set -o pipefail
cargo audit ${{ github.event_name == 'schedule' && '--deny yanked' || '' }} 2>&1 | tee "$RUNNER_TEMP/audit.log"
# The yank check fails open (cargo-audit 0.22.1 auditor.rs; verified
# locally, LAB-1152). "Updating crates.io index" prints
# unconditionally BEFORE the fetch attempt — a warm index cache
# cannot suppress it — so its presence proves only that the check
# was attempted (enabled, not disabled by e.g. a future `-n`). On
# fetch/open failure the tool warns "couldn't update|open crates.io
# index", skips the yank check entirely, and still exits 0; a
# per-package lookup failure warns "couldn't check if the package is
# yanked". The weekly run — the only one that enforces yanked —
# therefore needs both halves: the attempt line present AND every
# skip signal absent. Fail-closed on the pinned tool's wording is
# fine here: a wording change turns the weekly run red, never a PR.
if [ "${{ github.event_name }}" = "schedule" ]; then
if ! grep -q "Updating crates.io index" "$RUNNER_TEMP/audit.log"; then
echo "::error::cargo-audit never attempted the yank check (attempt line missing — check disabled?)"
exit 1
fi
if grep -qiE "couldn't (update|open) crates.io index|couldn't check if the package is yanked" "$RUNNER_TEMP/audit.log"; then
echo "::error::cargo-audit could not evaluate yanked crates (crates.io index unavailable or lookup failed)"
exit 1
fi
fi
Comment thread
27Bslash6 marked this conversation as resolved.
69 changes: 40 additions & 29 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ test-wasm:
CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=wasm-bindgen-test-runner \
$(CARGO) test -p cachekit-rs --target wasm32-unknown-unknown --no-default-features --features workers,cachekitio,encryption,macros --test wasm_session_tests

# Supply-chain gate — the same commands CI runs in
# .github/workflows/security.yml, so a local pass means a CI pass. (CI runs the
# audit step even when deny fails; make stops at the first failure.)
# Supply-chain gate — deny is identical to CI's; audit runs the strict form
# (`--deny yanked`, which CI applies only on the weekly schedule run), so a
# local pass covers every CI event in .github/workflows/security.yml. (CI runs
# the audit step even when deny fails; make stops at the first failure.)
# Kept out of `quick-check`: both tools fetch the RustSec advisory database over
# the network, which does not belong in a per-commit loop.
# Why both tools, and why --all-features: see the table in README.md.
Expand All @@ -38,4 +39,4 @@ deny:
$(CARGO) deny --locked --all-features check

audit:
$(CARGO) audit
$(CARGO) audit --deny yanked
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,11 @@ make build-wasm # wasm32-unknown-unknown (workers feature)
```

`make security` runs the same two commands as the `supply-chain` job in
`.github/workflows/security.yml`, so a local pass means a CI pass. It needs
`.github/workflows/security.yml`, with `cargo audit` in its strictest CI form
(`--deny yanked`) — so a local pass means a pass on every CI event, with one
asymmetry: the weekly run additionally proves the yank check actually executed
(see the guard in `security.yml`), so with crates.io unreachable a local run
warns and passes where the weekly run goes red. It needs
`cargo-deny` and `cargo-audit` installed, and it reaches the network to refresh
the RustSec advisory database — which is why it is not folded into
`quick-check`.
Expand All @@ -519,7 +523,8 @@ means it turns the check red — anything else is reported but not enforced:
| Reads | feature-resolved dependency graph | `Cargo.lock` verbatim |
| Licence allowlist, banned crates, registry/source policy | **fails** | not checked |
| Vulnerabilities in crates no enabled feature activates | not seen (pruned) | **fails** |
| Unsound / unmaintained advisories on *transitive* deps | not seen — `deny.toml` narrows `unmaintained` to `workspace`; `unsound` already defaults to that scope | reports only, does **not** fail |
| Yanked crates in `Cargo.lock` | warns (feature-resolved graph only, so lockfile-only crates are missed) | warns on PR and push runs; **fails** only the weekly scheduled run (`--deny yanked`) |
| Unsound / unmaintained advisories on *transitive* deps | not seen — `deny.toml` narrows `unmaintained` to `workspace`; `unsound` already defaults to that scope | reports only, does **not** fail — deliberate (see `deny.toml`) |

`--all-features` is load-bearing: the default feature set excludes the
`memcached`, `redis`, `file` and `macros` backends, so a banned crate
Expand Down
24 changes: 19 additions & 5 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,25 @@
# defaults to "all", unsound to "workspace" (direct workspace deps only).
# So the line below NARROWS unmaintained from its default. It is not a
# redundant restatement, and deleting it would tighten this gate.
# Consequence: transitive unsound/unmaintained advisories are not reported here
# (e.g. rand 0.8.5 / RUSTSEC-2026-0097, reached via fred and memcache).
# `cargo audit` in .github/workflows/security.yml lists them, but does not fail
# on them either — widening both scopes to "all" is open work, not covered.
# yanked: warn instead of hard-fail so renovate can fix.
#
# Warn-vs-fail posture per class (LAB-1152, decided 2026-08-31):
# - yanked: warn here, ENFORCED by `cargo audit --deny yanked` on the weekly
# schedule run in .github/workflows/security.yml. Audit is the enforcement
# point because cargo-deny grades the feature-resolved graph, which prunes
# lockfile-only crates — it scored 0 warnings while the lockfile held the
# yanked chacha20 0.10.1 (behind reqwest's unused quinn path). Warn, not
# deny, here so an upstream yank against an unchanged lockfile never blocks
# unrelated PRs. The weekly red is typically fixed same-day with
# `cargo update -p <crate>` — a yank almost always has a compatible
# non-yanked sibling; a full-crate or semver-gapped yank needs a dependency
# bump or replacement instead (rarer, still actionable).
# - unsound / unmaintained: warn, and the scopes stay narrow — widening to
# "all" would only duplicate warnings `cargo audit` already prints every run
# from the verbatim lockfile. Not enforced anywhere: neither class
# guarantees a fixed version exists (today's rand 0.8.5 / RUSTSEC-2026-0097,
# via fred 9.4 + memcache 0.19, has no compatible upgrade), so failing on
# them forces an ignore-list that accumulates — a worse posture than an
# honest warning.
yanked = "warn"
unmaintained = "workspace"

Expand Down