Remediate Image CVEs
Remediate actionable fixable vulnerabilities in the Linux CCM, CNM, and health-probe-proxy images for one input target branch.
Require the caller to provide an input branch that is exactly master or
matches ^release-[0-9]+\.[0-9]+$. Operate only on the current checkout;
callers may run this skill concurrently in separate worktrees for separate
input branches.
Sources of Truth
Read these repository skills before taking any action:
.agents/skills/build-images/SKILL.md.agents/skills/fix-image-cves/SKILL.md.agents/skills/sync-go-modules/SKILL.md
Follow those skills as the sources of truth for Makefile image builds and Trivy-based CVE remediation and for final repository-wide Go module consistency. Do not duplicate, override, or extend their commands. This skill supplies orchestration, lifecycle, Git, validation, cleanup, and pull-request policy only. If a dependency is missing or conflicts with this skill, stop and report the blocker.
Before checking out the input branch, copy this complete skill directory and
all three dependency skill directories into a unique run-scoped temporary
directory outside the repository. Use that exact tooling snapshot for the
entire run; do not depend on skill files present on the target branch.
Invoke both image helpers and the module-sync helper with their explicit
--repo input pointing at the current worktree. Remove the temporary tooling
snapshot during final cleanup.
Git Lock Discipline
Run every orchestration-owned read-only Git inspection with command-scoped
GIT_OPTIONAL_LOCKS=0. This includes status, diff, log, show, rev-parse,
ls-files, ls-tree, and ref checks. The image-CVE and module-sync helpers enforce
the same rule internally for their read-only Git subprocesses.
Do not export this setting across the workflow or pass it to mutating Git commands. Fetch, checkout, branch or ref updates, staging, commits, and pushes must run serially with normal required locking. Before each mutation and after each long-running helper exits, inspect the current worktree Git directory for lock files. If an unexpected lock exists, stop and report its path, owner, size, modification time, and holder check when available. Never delete, rename, bypass, or work around it as part of this skill.
Preconditions
- Require a clean worktree before changing branches. Preserve all pre-existing work; never reset, discard, or overwrite it.
- Fail fast if Git, GitHub CLI, Trivy, Docker with Buildx or Podman with native build support, or a local Go version compatible with the checked-out branch is unavailable. Before each CVE apply, also require an available local Go version compatible with the fresh plan's Go directive actions. Do not install or upgrade host software or enable automatic toolchain downloads.
- Require both
git var GIT_AUTHOR_IDENTandgit var GIT_COMMITTER_IDENTto succeed before any build so checkpoint commits cannot fail after remediation has started. - Require
upstreamto contain the input branch and verify, before any build, that the current authenticated account can push tooriginand open a pull request againstupstream. Check repository permissions rather than hard-coding classic-token scope names. - Check for an existing open CVE-remediation pull request targeting the input branch. If one exists, stop and report its URL instead of creating a duplicate.
- If this worktree already has unfinished
fix-image-cvesstate, stop and report it instead of deleting evidence from an earlier run.
Branch and Image Identity
Validate that the input branch is exactly master or matches
^release-[0-9]+\.[0-9]+$. Fetch and check out its current tip from upstream,
using only a fast-forward update if a local branch already exists. Never
hard-reset a local branch; stop if it cannot be fast-forwarded safely. Then
create a branch named
cve-fix-<input-branch>-<UTC timestamp>, for example
cve-fix-master-20260721T013000Z or
cve-fix-release-1.36-20260721T013000Z.
Use the build skill with:
- image registry:
local - image tag:
cve-<short-sha>-<UTC timestamp>-<run-id> - explicit make inputs:
ARCH=amd64andOUTPUT_TYPE=docker - inherited make inputs removed:
OUTPUT_FLAGandBUILDX_EXTRA_FLAGS
Generate <run-id> once as a lowercase UUID without hyphens and retain it for
the complete run. The tag must be unique to the run so concurrent worktrees
cannot overwrite, scan, or delete one another's verification images. Pass
those make inputs through the build skill's --set and --unset options on
every build, including rebuilds and final verification builds. These images
are disposable and must never be pushed.
Select one container runtime for the complete run. Respect an explicit
CONTAINER_CLI; otherwise use the Makefile preference of Podman when available
and Docker otherwise. Resolve and record its absolute path, then pass it through
the build skill as --set CONTAINER_CLI=<path> on every build. Do not build with
one runtime and scan or clean up with another.
After each build, use the selected runtime to resolve and record the canonical
local image reference. Use that exact reference for Trivy scans, rescans, and
cleanup. Podman may normalize local/... to localhost/local/...; do not
assume the Makefile reference is also the canonical runtime reference.
Verification Set
Use this order and identity in every phase:
| Image | Build alias | Makefile image reference | Module root | Dockerfile |
|---|---|---|---|---|
| CCM | ccm |
local/azure-cloud-controller-manager:<tag> |
. |
Dockerfile |
| CNM | cnm |
local/azure-cloud-node-manager:<tag>-linux-amd64 |
. |
cloud-node-manager.Dockerfile |
| health-probe-proxy | hpp |
local/health-probe-proxy:<tag> |
health-probe-proxy |
health-probe-proxy/Dockerfile |
For every build, allow concurrent builds from other worktrees. Docker builds
share a host-level Buildx builder; Podman uses its native builder. Pass the
build helper's --retry-transient-runtime-errors option on every build. The
build skill owns runtime-specific failure classification, health checks,
bounded delay, exact retry execution, and the two-attempt limit. Do not run an
additional outer retry. If the helper still returns a failure, preserve both
attempts in the run summary and stop.
Baseline Phase
Establish the three-image baseline before mutating source:
- Build all three images from the untouched input-branch source before running
any
fix-image-cves applycommand. - For each baseline image, run
scanandplanwith its canonical image reference, module root, and Dockerfile. Record the complete plan, actionable findings, unsupported fixable findings, and residual risks outside the helper state before scanning the next image. - Treat Go toolchain findings and findings without a
FixedVersionas residual risks. Treat any OTHER finding with a non-emptyFixedVersionas an unsupported fixable finding and stop without applying changes or opening a PR, preserving that image's state. - The helper has one worktree-local state file. When continuing, run its
cleancommand after recording each successful baseline result so the next image starts with empty state. If a baseline scan or plan fails, preserve that image's state and stop.
If all three baseline plans contain zero Go-module actions, zero base-image actions, and zero unsupported fixable findings, exit successfully without committing, pushing, or opening a PR. Only residual findings may remain on this no-change path.
Remediation Phase
After the complete baseline is recorded, process images sequentially in the table order:
- Rebuild the image from the current source and resolve its canonical runtime
reference. Run a fresh
scanandplan; do not apply the saved baseline plan because an earlier image may already have changed a shared module. - Review every fresh plan. If it contains Go directive actions, select a
locally installed Go version at least as new as the highest target before
apply, and keep
GOTOOLCHAIN=local. Apply Go-module and directive fixes throughfix-image-cves, including compatible pinned Go builder targets for every affected verification Dockerfile (both CCM and CNM for the root module). Follow the fix skill'sDockerfile:buildertarget policy; keep already-compatible builders unchanged. For a fixable runtime base-image finding, automatically replace only the digest of the existing registry, repository, and tag. If runtime base-image remediation would change the image family or tag, stop for review. Stop and report any other permission, judgment, or conflict-resolution requirement instead of guessing. - If the fresh plan contains no Go-module or base-image actions and no
unsupported fixable findings, record its residual risks, run
clean, and continue to the next image. Skipapply, file verification, remediation rebuild, and planned-key rescan for this zero-action plan. - If the fresh plan has actions, run
apply, then file verification. Rebuild the image and runverify --rescanto prove the planned vulnerability keys disappeared. - Before starting a new scan, record the apply state's exact modified-file
list and verification results;
scanreplaces prior apply and verify state. Then run a freshscanandplanof the rebuilt image to detect newly introduced actionable or unsupported fixable findings. - After the fresh plan is reviewed, commit only the recorded modified files as the checkpoint for that cycle. Do not commit immediately after file verification. The fresh plan need not be empty for an intermediate cycle. Never reset or revert an earlier checkpoint, and keep all checkpoint commits in the final PR.
- If the fresh plan has no remaining actions or unsupported fixable findings,
record residual risks, run
clean, and continue to the next image. If it has more actions, use that current plan for the next cycle. Allow at most three apply/verify/fresh-plan cycles per image. - If verification fails, an unsupported fixable finding appears, or actions remain after the third cycle, mark the run incomplete. Stop without pushing or opening a PR and preserve the current helper state, source changes, and local checkpoint commits for diagnosis.
Final Gate
After all remediation cycles:
- From the clean checkpointed source, run
sync-go-modulesonce to normalize every tracked module and the root vendor tree exactly as CI does. If the vendor dependency set changes, rerun it with--allow-dirtyand--update-vendor-licenses, then run it once more with--allow-dirtyto normalize the root module after license generation. Inspect the resulting diff and commit only expectedgo.mod,go.sum,vendor/, andLICENSES/paths as a final module-consistency checkpoint. Never absorb unrelated files. - Run
sync-go-modules --check-clean. Do not continue unless it produces no diff. If vendor-license generation was needed, normalize modules again after that generation before running--check-cleanbecause the upstream license tooling executesgo list -m all. - Run the full repository unit-test suite with
make test-unit. - Run
git diff --checkagainst the input branch. - Rebuild all three images from the final committed source.
- Freshly scan and plan all three rebuilt images in table order. Record each
result before scanning the next image; a new
scanreplaces the previous state, so no intermediatecleanis required. Stop immediately on failure so the failing image's current state remains available. Verification is complete only when none has remaining Go-module or base-image actions and none has an unsupported fixable OTHER finding. Preserve findings without a fixed version and Go toolchain findings as residual risks. - Require a clean worktree containing only the checkpoint commits relative to the input branch.
If any gate fails or actionable or unsupported fixable vulnerabilities remain, do not push or open a PR. Preserve the local branch, checkpoint commits, current CVE state, and a precise failure summary.
Pull Request
When source changes exist and every final gate passes:
- Push the CVE-fix branch to
origin. - Open a ready-for-review PR in
kubernetes-sigs/cloud-provider-azuretargeting the input branch. - Treat
.github/PULL_REQUEST_TEMPLATE.mdin the target checkout as authoritative. Start from its exact contents and preserve every section heading, the issue field, the fencedrelease-noteanddocsblocks, and any additional sections it contains. HTML guidance comments may be removed only after following their instructions. Never replace or drop repository template structure to match the skill asset; stop if the two cannot be merged without losing required content. - Use title
chore: fix cves for <input-branch>. Readassets/pull-request-template.mdfrom the run-scoped snapshot of this skill, and replace every placeholder. UnderWhat this PR does / why we need it, useRemediates actionable fixable vulnerabilities in the Linux CCM, CNM, and health-probe-proxy images for <input-branch>., followed by the populated results table and validation block from the asset. UnderSpecial notes for your reviewer, use only the populated residual-risk details block from the asset. Do not use the asset as the complete PR body or copy its content into any other section. - Preserve the repository template's heading text and order. Before opening
the PR, verify that every
####heading from the repository template appears exactly once in the same order and that itsrelease-noteanddocsfences remain. Use/kind cleanup, no issue closure unless the caller supplied one, andNONEin the release-note block. Replace the issue placeholder withNONEwhen no issue was supplied. Count vulnerability findings rather than grouped remediation actions in the baseline column. UseNoneinstead of omitting an empty fix or residual-risk entry. For a shared root-module fix, identify the checkpoint that covers both CCM and CNM.
Cleanup and Output
In a final cleanup step on both success and failure, use the selected runtime to remove only the three recorded canonical references for this run's uniquely tagged local verification images. Ignore an image that was never created. Never run a system prune, remove shared base layers, clear Buildx cache, or delete images belonging to another run.
Clean fix-image-cves state after complete success. Preserve it after failure.
On the no-change path, clean the state and remove the unused local CVE-fix
branch.
Output:
| Image | Baseline findings | Applied fixes | Final verification | Residual risks |
|---|
Then report the module-consistency result, unit-test result, checkpoint commits, cleanup result, and PR URL, or the precise reason no PR was created.