--- name: cve-remediator-v2 description: >- Raise Go modules to caller-supplied minimum fixed versions from CVE/GO findings in any format, per tracked module root, sync go.mod/go.sum and root vendor/, audit the source module graphs, run the given checks, and publish a PR, all enforced by one script. Use when the user gives vulnerability findings (a table, list, prose or records) naming the affected component or dependency, the CVE/GO ID and the fixed version, and wants source-only Go dependency bumps without building or scanning images. --- # CVE Remediator v2 (source-only) `scripts/cve_remediator_v2.py` generates, audits and validates the change and, with `--publish`, commits, pushes, and opens or updates the PR in one run. It enforces the scope, source identity, branch, PR and readback rules itself. It never fetches advisories, scans images, installs tools, rolls back or retries. Use `fix-image-cves` for images. Replace `` with this skill directory. ## Your decisions - **Scope:** keep only findings that apply to the chosen checkout's Linux components and releases, whatever the input layout. Never infer branches from image tags. If the scope is unclear or nothing applies, ask. - **Mode:** ordinary remediation uses `--publish`. Use `--local-only` or `--dry-run` only for local-only, no-push, read-only or preview requests. - **Checks:** real build and unit test commands for the affected roots that actually rebuild (for example `make -B`). Never all-root `go test` or live e2e. - **Gaps:** only for checks the caller accepted as not run, never for a failed check. Reasons are published in the PR; keep them truthful and public-safe. - **Licenses:** ordinary remediation includes needed `LICENSES/` snapshots. A refresh is needed when planned bumps add vendored module paths or change license files not covered by existing snapshots; then set `licenses.needed` to true with `licenses.refresh` argv `["python3", "/scripts/license_refresh_tidy.py", "--", "make", "update-vendor-licenses"]` and cwd `"."`. The repo command downloads upstream Kubernetes scripts, so it needs network. The wrapper then removes only the `go.mod` checksum records the generator adds to root `go.sum`, and fails without tidying on any other change outside `LICENSES/`. If the user excluded licenses or network, or the command is unavailable, ask before generating; use a `licenses` gap only if the user accepts one. Never refresh for preview or read-only requests. - **PR:** explicit targets, and a truthful public title and body based on the target checkout's `.github/PULL_REQUEST_TEMPLATE.md` and [pull request guidance](../../references/pull-requests.md). The runtime adds the release tag to the title and writes the description and reviewer notes (see Inputs). ## Inputs The F, V, P and body files live outside the checkout, and the checkout must be clean. The selected Go (`--go`) runs generation and the sync, and its directory is first on `PATH`, so a bare `go` in checks uses it. For `--local-only` and `--publish` it must be an executable named `go` (a symlink is fine), new enough for every tracked `go` directive and every target. `--dry-run` accepts any name. Findings (F) is JSON you prepare from the user's findings; the user need not supply it. Read the input by meaning, not by layout or column names. Each finding should give a vulnerability ID, what is affected, and a fixed version. - Write one row per vulnerability, affected Go dependency and affected tracked root. CCM and CNM share the root module `.`; HPP is `health-probe-proxy`. Confirm that any other component maps to a tracked root, or ask. Keep each row's own fixed version, and keep different dependencies that share an ID as separate rows. - `package` is the affected Go dependency, never the product. A label such as "component" may name either; decide by meaning. If the dependency is not given, identify it from the supplied details, the checkout or a reliable reference, and ask if it is still unclear. Never guess it from the ID or version. Set `module` only when the exact module path is known. - `fixed` is the supplied fixed version or comma-separated list, unchanged; never choose among candidates yourself. Use `N/A` only when the finding says there is no fixed version; if it is missing or unclear, ask. - `id` is an opaque label. `installed` is the scanned version. It is required (exact semver) when `fixed` lists several versions; otherwise provenance only, `""` if not given. The helper performs no CVE or advisory lookups. A single `fixed` version is the row's floor. For a list, it picks the lowest version newer than `installed`. Per root and module the highest row floor wins and is checked against the resolved checkout. A list blocks if `installed` is missing or invalid or no listed version is newer. Every listed version must be an exact Go semver of the module's major version. ```json {"findings": [ {"id": "CVE-2026-81870", "package": "go.opentelemetry.io/otel/sdk", "installed": "v1.44.0", "fixed": "1.45.0"}, {"id": "GO-2026-5932", "package": "golang.org/x/crypto", "installed": "", "fixed": "N/A"} ]} ``` `module_root` defaults to `.`; set it for any other root. Validation (V): ```json {"checks": [ {"name": "ccm-build", "group": "build", "argv": ["make", "-B", "bin/azure-cloud-controller-manager"], "cwd": "."}, {"name": "root-unit", "group": "unit", "argv": ["make", "test-unit"], "cwd": "."}], "gaps": [], "licenses": {"needed": false, "refresh": null}} ``` `build` and `unit` each need at least one check, or a gap instead (`{"group": "unit", "reason": "..."}`), never both. If a refresh is needed, give `"refresh": {"argv": [...], "cwd": "."}` or a `licenses` gap. A refresh may change only `LICENSES/`. Publish (P): ```json {"host": "github.com", "base_repo": "kubernetes-sigs/cloud-provider-azure", "base_remote": "upstream", "base": "master", "head_repo": "/cloud-provider-azure", "remote": "origin", "head": "cve-fix-otel", "title": "chore: bump otel for CVE-2026-81870", "body_file": "/tmp/pr-body.md"} ``` The head must be a dedicated branch, and remote URLs must match exactly, with no ports. A fork head must be a personal, direct fork of the base with the same repository name. Give `title` without a release tag. For a `release-X.Y` base the runtime publishes `[release-X.Y] `, for example `[release-1.33] chore: bump otel for CVE-2026-81870`; a leading tag that already names the base is not repeated. A leading `[release-...]` tag that does not name the base branch, a repeated tag, or a tag-only title blocks the run. The body file must keep each `####` heading of the checkout's `.github/PULL_REQUEST_TEMPLATE.md` exactly once; without that template the run cannot publish. Fill the kind, issue, release-note and other required sections. The runtime replaces everything under "What this PR does / why we need it:" with a fixed intro and one line per proved fix (see Report), for example: ```markdown Raises Go dependencies in the source module graphs to at least the reported fixed versions: - CVE-2026-81870: go.opentelemetry.io/otel/sdk v1.45.0 ``` A fix in a root other than `.` ends with the root, such as `(health-probe-proxy)`. "Special notes for your reviewer:" gets only the `Unresolved:` (residual and pruned rows) and `Not run (accepted):` (gap groups and reasons) lists that apply, or stays empty. Check names, argv, logs and other diagnostics stay in the local report. ## Run ```bash python3 <SKILL_DIR>/scripts/cve_remediator_v2.py --repo <checkout> --findings F.json [--go <go>] --dry-run python3 <SKILL_DIR>/scripts/cve_remediator_v2.py --repo <checkout> --findings F.json [--go <go>] --validation V.json --local-only python3 <SKILL_DIR>/scripts/cve_remediator_v2.py --repo <checkout> --findings F.json [--go <go>] --validation V.json --publish P.json --gh <absolute-gh> ``` ## Report - **Status and exit code:** - exit 0: `dry-run`, `validated`, `no-change` or `published` - exit 2: `blocked`, before any source change (ref fetches and Go caches may still have changed) - exit 1: `failed`; `stage` and `remaining_state` say where it failed and what was left behind - Nothing is rolled back. A rerun on a clean tree repeats everything. - **Rows:** `raised` and `satisfied` mean the floor is met. `residual` and `pruned` stay unresolved. - **PR:** a new PR is ready for review; an existing PR keeps its draft or ready state and is never converted. A known PR appears in `publication.pr` as soon as it is found or created, and `verified` becomes true only after readback passes; readback also checks the exact title, and the body ignoring CRLF and trailing whitespace. `publication.title` and `publication.fixes` record the published title and the proved fixes. - **Proved fixes:** a `raised` or `satisfied` row is listed only if its own floor is newly met: its baseline is below the floor and `selected_after` meets it. A row already met at the baseline is not listed, even when another row's higher floor lifted the module. The baseline is `selected_before`; when the checkout already has PR-range commits (for example on a rerun), it is the merge-base graphs, which the selected Go lists from copies of the merge base's committed `go.mod` and `go.sum` files outside the checkout (Go may download module metadata into its cache). A missing root or module, or a replaced module, proves nothing. - **Refusals:** title and body-file problems block (exit 2) before generation. The run fails at stage `range`, before any commit or push and keeping the generated changes, when no fix is proved or the merge-base graphs cannot be listed safely: for example an untidy `go.mod`, a `go.mod` or `go.sum` that is not a regular file, or a directory replacement outside the copied layout. A genuine `no-change` run still exits 0. - **What to claim:** each selected minimum is met, or the module is no longer selected, in the resolved source module graphs, and the listed checks passed. Never claim CVEs are cleared or images or deployments verified.