/cve-check answers 'what are we exposed to that an upgrade would fix?' without running an upgrade: per-recipe, resolve the available window for EVERY image (sidecars included), run the advisory scan over it, adjudicate whatever pass 1 could not decide, publish a report. Read-only — no PRs, no CI, no merges. /cve-check-and-upgrade does that sweep, then runs /recipe-upgrade only on the recipes whose upgrade actually closes a CVE, worst severity first, and reports on both. --min-severity high for just the urgent ones; --dry-run prints the queue and stops. Never merges. Deliberate choices, each written into the skills: - externals are SWEPT but never upgraded here — a security sweep that skipped deployed software would misreport exposure, but we don't maintain them. - an unknown count never justifies an upgrade AND is never treated as clean; it goes to the Addendum. - no upgrade available means 0 CVEs, not '?'. - subagents are told which CVEs justify their upgrade, so the PR says why it exists — a PR naming the RCE it closes gets reviewed sooner. recipe-report.py grows a page kind: 'cve' files as cve-DATE.html so a sweep can't overwrite a weekly edition, while BOTH appear in the same archive index, suffixed 'full report' / 'CVE check'. /help and /cc-ci-status updated to route to them.
92 lines
6.0 KiB
Markdown
92 lines
6.0 KiB
Markdown
---
|
||
name: cve-check-and-upgrade
|
||
description: Security-driven upgrade run. Does a full /cve-check sweep first (per-image advisory scan of every recipe's available upgrade, with adjudication), then runs /recipe-upgrade ONLY on the recipes whose upgrade fixes at least one CVE — worst severity first — opening a verified recipe PR for each, and finally publishes one report covering both the sweep and the PRs. Recipes with no CVEs are left alone; that is the point. NEVER merges. Invoke as /cve-check-and-upgrade [recipe ...] [--min-severity high] [--capacity N] [--dry-run].
|
||
---
|
||
|
||
# cve-check-and-upgrade
|
||
|
||
`/upgrade-all` upgrades everything that *has* an upgrade. **This upgrades what has a reason.** It runs
|
||
the `/cve-check` sweep, then spends CI time only on the recipes where an upgrade actually closes a
|
||
vulnerability, handling the worst first.
|
||
|
||
Use it when you want to act on security rather than churn the whole fleet: after a vendor announcement,
|
||
when CI capacity is short, or between weekly runs. When you only want to *know*, use `/cve-check`. When
|
||
you want everything current regardless of CVEs, use `/upgrade-all`.
|
||
|
||
**Creates PRs. Never merges.** Every PR is verified green on cc-ci and left for a human.
|
||
|
||
## Arguments
|
||
- `<recipe> …` — restrict the whole run to these recipes.
|
||
- `--min-severity critical|high|medium|low` — only upgrade recipes whose fixed CVEs reach this
|
||
severity. Default **`low`** (any CVE at all justifies the upgrade). `--min-severity high` is the
|
||
useful "just the urgent ones" setting.
|
||
- `--capacity N` — subagent pool size; defaults to the live `DRONE_RUNNER_CAPACITY` (the drone
|
||
runner's slots), matching `/upgrade-all`'s rolling-pool behaviour.
|
||
- `--dry-run` — do the whole sweep and print exactly which recipes *would* be upgraded and why, then
|
||
stop without spawning a single upgrade. **Publishes no report and opens no PR.**
|
||
|
||
## Procedure
|
||
|
||
### 1. Sweep — run `/cve-check` in full
|
||
Follow `.claude/skills/cve-check/SKILL.md` steps 1–5 exactly: candidate list, per-image upgrade windows,
|
||
the advisory scan per recipe, pass-2 adjudication of anything undecided, and the severity classification.
|
||
**Do not publish its report** — this run produces one combined report at the end instead.
|
||
|
||
Keep, per recipe: the windows scanned, the CVE count, the CVE ids with severities, and whether the count
|
||
is a floor (undetermined advisories remain) or unknown.
|
||
|
||
### 2. Decide what to upgrade
|
||
`RECIPES_TO_UPGRADE` = recipes where **the scan found ≥1 CVE** at or above `--min-severity`.
|
||
|
||
Deliberate exclusions, each recorded in the report with its reason:
|
||
- **0 CVEs** — an upgrade may exist, but nothing security-relevant. Left alone; that is the point of
|
||
this skill. `/upgrade-all` is what sweeps those up.
|
||
- **`external` tier** — swept for visibility, **never upgraded here**; someone else maintains it. Flag
|
||
it loudly in the report if it has a critical, since the action is to tell them, not to open a PR.
|
||
- **`UPTODATE`** — nothing available.
|
||
- **count `?` / UNKNOWN** — do **not** upgrade blind, and do **not** treat it as clean. Put it in the
|
||
Addendum as needing a look. An unknown is a gap in our knowledge, not evidence of safety.
|
||
|
||
Order the queue by **worst severity first** (critical → high → …), count breaking ties. If `--dry-run`,
|
||
print this queue with each recipe's CVE ids and severities, and STOP here.
|
||
|
||
### 3. Upgrade each one — via `/recipe-upgrade` subagents
|
||
Run `/recipe-upgrade <recipe>` per queued recipe as a **subagent**, in the queue order above, as a
|
||
**rolling pool** keeping `--capacity` (default `DRONE_RUNNER_CAPACITY`) running at once and starting the
|
||
next as each finishes — the same concurrency discipline as `/upgrade-all` §3, and safe for the same
|
||
reason (per-run recipe trees + app-domain locks).
|
||
|
||
Each subagent does the full job: plan, implement the bump, verify green on cc-ci with `!testme`, and
|
||
open a recipe PR. **Default mode — no `--with-tests`**: a genuinely stale test gets an explanatory PR
|
||
comment, not a test edit.
|
||
|
||
**Tell each subagent which CVEs justify its upgrade**, with ids and severities, so the PR description
|
||
says why it exists. That is most of this skill's value to a reviewer: a PR that names the CVSS-9.8 RCE
|
||
it closes gets merged today, an unexplained version bump waits a fortnight.
|
||
|
||
Collect per recipe: PR url + number, the `!testme` verdict and build number, and any failure.
|
||
|
||
### 4. Report — one page covering sweep AND PRs
|
||
Write `/tmp/cve-spec-<DATE>.json` per `/cve-check` step 6, with these differences:
|
||
- Rows for upgraded recipes carry the real `ci` (`build N ✓` / `RED N · <stage>`) + `ci_url`, and
|
||
`pr`/`pr_url`. `status` is the CI verdict (`GREEN`/`FAILED`/`STALE`); the live PR-status column
|
||
derives itself from `recipe` + `pr`.
|
||
- Rows for swept-but-not-upgraded recipes keep `PENDING`/`UPTODATE` with empty `ci`/`pr`, and a
|
||
`notes` reason (`0 CVEs — not upgraded`, `external — maintained elsewhere`, `below --min-severity`).
|
||
- Include `changes[]` — one entry per recipe that got a PR, describing what the upgrade changes **and
|
||
the CVEs it closes**.
|
||
- Keep `"kind": "cve"`: it titles the page "The Recipe Report — CVE check" and files it as
|
||
`cve-<DATE>.html`, alongside the weekly editions in the same archive index.
|
||
|
||
Then render + publish exactly as `/cve-check` step 7, and verify as its step 8. Print the report URL,
|
||
`N swept · M upgraded · K PRs green · J failed`, and `CVE CHECK AND UPGRADE COMPLETE`.
|
||
|
||
## Guardrails
|
||
- **NEVER merge.** Create and verify; a human merges. Never push to true upstream.
|
||
- **Never weaken a test** to make a PR green, and never edit a test without `--with-tests`.
|
||
- **Never upgrade a recipe whose CVE count is unknown** on the assumption it is fine — surface it.
|
||
- **Never upgrade an `external` recipe** here, even with a critical; report it instead.
|
||
- **Public-safe report only** — no secrets, tokens, internal hostnames, raw logs, or spend figures.
|
||
- If the sweep finds **nothing** at or above `--min-severity`, that is a good outcome: publish the
|
||
report saying so and open no PRs. Do not manufacture work.
|