From 2a5d40c6d9e37261c662571cb62e39d8ce622ff2 Mon Sep 17 00:00:00 2001 From: Ramkumar Ethiraj <64294490+rmkr-dev@users.noreply.github.com> Date: Tue, 27 Oct 2026 05:07:56 +0000 Subject: [PATCH 1/2] Add build provenance and image verification skill --- skillbook/skills.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/skillbook/skills.json b/skillbook/skills.json index 8b3f179..36c5d6d 100644 --- a/skillbook/skills.json +++ b/skillbook/skills.json @@ -797,6 +797,26 @@ ], "formatNotes": "All three. Copilot: path-scoped `.github/instructions/kustomize.instructions.md` with applyTo for `**/kustomization.yaml`, `**/kustomization.yml`, `deploy/**/*.yaml`, `k8s/**/*.yaml`, and `.github/workflows/*k8s*.yml`. Optional `.github/skills/kustomize-aks-overlay-guardrails/SKILL.md`. Claude Code: `.claude/skills/kustomize-aks-overlay-guardrails/SKILL.md` plus a CLAUDE.md fact that every overlay must build without warnings, pass kubeconform and the policy check, and show its rendered diff in the PR. Codex: a \"Kubernetes manifests\" section in root `AGENTS.md` or a nested one in the deploy folder. Pair with aks-workload-hardening for pod security, k8s-probes-graceful-shutdown-guardrails and aks-hpa-requests-limits-guardrails for what goes inside the Deployment, aks-workload-identity-guardrails and azure-key-vault-app-integration for identity and secrets, and helm-chart-aks-guardrails if a chart is also in play. This skill owns manifest layout, Kustomize fields, patches, generators, and the render-validate-diff CI gate.", "body": "# Skill: Kustomize Overlay Guardrails for AKS\n\n## Goal\nOne base and a small overlay per environment render the exact manifests that reach each AKS cluster. Every overlay builds cleanly, validates against the cluster's Kubernetes version, and shows its rendered diff in the pull request, so a reviewer sees what will change in the cluster rather than guessing from patch fragments. No secret values live in Git, images are pinned, and selector labels never change after the first deploy.\n\n## When to apply\n- Starting Kubernetes manifests for a new service, or replacing a Helm chart with plain manifests plus Kustomize\n- Adding a new environment (dev, test, stage, prod) or a new cluster or region\n- Reviewing a pull request that touches `kustomization.yaml`, a patch, a generator, or a component\n- Seeing deprecation warnings (`bases`, `commonLabels`, `patchesStrategicMerge`, `patchesJson6902`, `vars`) from `kustomize build`\n- An apply failed with \"field is immutable\" on a Deployment selector, or a ConfigMap change did not roll the pods\n\n## Baseline rules\n1. **Layout is base, components, overlays.** `base/` holds environment-neutral resources. `overlays//` holds only what differs: namespace, replicas, resources, image tag, config values. `components/` holds optional cross-cutting pieces (for example monitoring annotations, a PodDisruptionBudget, a high-availability topology spread) that some overlays opt into. An overlay never copies a whole resource from the base.\n2. **Use current fields only.** `resources` instead of `bases`, `labels` instead of `commonLabels`, `patches` instead of `patchesStrategicMerge` and `patchesJson6902`, `replacements` instead of `vars`. Run `kustomize edit fix` on old files and diff the rendered output before and after the migration.\n3. **Choose the patch type by intent.** Strategic merge patches for changing values; they read like the resource. JSON 6902 patches with an explicit `target` for list edits and removals, because strategic merge replaces lists without a merge key (`args`, `command`) wholesale. Keep each patch small and named for what it changes (`replicas.yaml`, `resources.yaml`).\n4. **Set selector labels once.** Put the stable identity labels (`app.kubernetes.io/name`, `app.kubernetes.io/instance`) in the base resources' selectors directly. Add common labels with `labels:` and `includeSelectors: false` unless you are creating the Deployments for the first time, because a Deployment's selector is immutable and changing it later forces delete-and-recreate.\n5. **Pin images in one place.** Set image names and tags or digests with the `images:` field in the overlay (or a CI step running `kustomize edit set image`), never by patching the container spec. Production uses a digest or an immutable tag from your registry; `latest` never appears in rendered output.\n6. **Let generators roll config.** Generate ConfigMaps with `configMapGenerator` and keep the default hash suffix, so a config change produces a new name and rolls the pods. Don't set `disableNameSuffixHash: true` unless something outside Kustomize references the name, and document why when you do.\n7. **No secret values in Git.** Don't use `secretGenerator` with literal values or committed files for real secrets. On AKS, mount secrets from Azure Key Vault through the Secrets Store CSI driver with workload identity, or use an operator you already run; the overlay only references the SecretProviderClass and the identity.\n8. **Pin remote resources.** Avoid remote bases. If you must use one, pin it to a tag or commit (`?ref=`) and review its rendered output like your own code. Don't enable Helm inflation inside Kustomize (`--enable-helm`) as a shortcut; render the chart separately if you truly need it.\n9. **Validate the rendered output in CI.** For every overlay: `kustomize build` with the same Kustomize version you deploy with (the one embedded in `kubectl` can lag the standalone binary), then schema validation against the target Kubernetes version (for example kubeconform in strict mode, with CRD schemas for your custom resources), then a policy check (for example kube-linter or your admission policies run locally). Fail the build on any error or deprecation warning.\n10. **Show the rendered diff in the pull request.** CI renders each overlay on the base branch and on the PR branch and posts or uploads the diff. Reviewers approve rendered changes, especially for prod.\n11. **Keep overlays boring.** No logic, no templating, no environment-specific copies of base resources. If two overlays need the same change, it belongs in the base or a component.\n\n## Workflow\n1. **Map.** List every overlay, what it changes from the base, the target cluster and namespace, and the Kubernetes version of each cluster.\n2. **Modernize.** Run `kustomize edit fix`, replace deprecated fields, and confirm the rendered output for each overlay is byte-identical, or explain each difference.\n3. **Restructure.** Move duplicated overlay content into the base or a component. Move image settings to `images:` and config to generators.\n4. **Remove secrets.** Replace any committed secret material with Key Vault references through the CSI driver and workload identity, and rotate anything that was ever committed.\n5. **Guard.** Add the CI job from rules 9 and 10 for every overlay, using pinned tool versions.\n6. **Verify.** Run `kubectl diff -k overlays/` against a non-production cluster, then deploy and watch the rollout.\n\n## Review checklist\n- [ ] Base, components, and overlays are separate; no overlay copies a full base resource\n- [ ] No deprecated fields; `kustomize build` prints no warnings\n- [ ] List edits and removals use JSON 6902 patches with explicit targets\n- [ ] Selector labels are stable; added labels use `includeSelectors: false`\n- [ ] Images are set with `images:`; prod uses a digest or immutable tag; no `latest`\n- [ ] ConfigMaps come from generators with hash suffixes left on\n- [ ] No secret values in Git; secrets come from Key Vault via the CSI driver and workload identity\n- [ ] Remote resources are absent or pinned to a ref\n- [ ] CI builds, schema-validates, and policy-checks every overlay with pinned tool versions\n- [ ] The PR shows the rendered diff for each changed overlay\n\n## Copy-ready SKILL.md stub\n```markdown\n---\nname: kustomize-aks-overlay-guardrails\ndescription: Use when writing or reviewing Kustomize manifests for AKS or any Kubernetes cluster — base, components, and per-environment overlays; current fields only (resources, labels, patches, replacements); strategic merge for values and JSON 6902 for list edits; stable selector labels; images pinned with the images field; ConfigMap generators with hash suffixes; no secret values in Git; pinned remote resources; every overlay built, schema-validated, and policy-checked in CI with its rendered diff shown in the pull request.\n---\n\nFollow Kustomize Overlay Guardrails: keep environment-neutral resources in base, optional cross-cutting pieces in components, and only real differences in each overlay. Use resources, labels, patches, and replacements, never the deprecated fields. Use strategic merge patches for values and JSON 6902 patches with explicit targets for list edits and removals. Never change Deployment selector labels after the first deploy; add labels with includeSelectors false. Set images with the images field and pin prod to a digest or immutable tag. Generate ConfigMaps with hash suffixes so config changes roll pods. Keep secret values out of Git and mount them from Azure Key Vault through the CSI driver and workload identity. Build, validate, and policy-check every overlay in CI with pinned tool versions, and show the rendered diff in the pull request.\n```\n" + }, + { + "id": "artifact-provenance-signing-guardrails", + "title": "Build Provenance and Image Verification Guardrails", + "whenToUse": "Adding or reviewing build provenance, SBOM attestations, or image signing for container images, JARs, or binaries built in GitHub Actions and deployed to AKS — digest-only artifact identity, keyless OIDC attestation with actions/attest instead of stored keys, a least-privilege attest job, honest SLSA level claims with reusable workflows for L3-style isolation, SBOMs from the built artifact, exact-identity `gh attestation verify` before deploy, private-repo trusted roots, attestations that travel on promotion, and AKS admission verification rolled out audit-first.", + "tools": [ + "copilot", + "claude-code", + "codex" + ], + "tags": [ + "supply-chain", + "slsa", + "github-actions", + "aks", + "devsecops", + "security" + ], + "formatNotes": "All three. Copilot: path-scoped `.github/instructions/supply-chain.instructions.md` with applyTo for `.github/workflows/*.yml`, `**/Dockerfile`, and `deploy/policies/**/*.yaml`. Optional `.github/skills/artifact-provenance-signing-guardrails/SKILL.md`. Claude Code: `.claude/skills/artifact-provenance-signing-guardrails/SKILL.md` plus a CLAUDE.md fact that deploys use digests verified with `gh attestation verify --repo --signer-workflow`. Codex: a \"Release and supply chain\" section in root `AGENTS.md`, or the same SKILL.md under `.agents/skills/`. Pair with gha-devsecops-pipeline for action pinning and scan gates, gha-oidc-azure-guardrails for the Azure login that pushes to ACR, container-image-hardening for the Dockerfile itself, maven-dependency-cve-hygiene-guardrails for CVE triage, and kustomize-aks-overlay-guardrails for pinning the verified digest in overlays. This skill owns attestation, verification identity, promotion, and admission enforcement.", + "body": "# Skill: Build Provenance and Image Verification Guardrails\n\n## Goal\nEvery artifact you ship (container image, JAR, CLI binary) carries signed build provenance that says which repository, commit, and workflow produced it, plus an SBOM. The deploy pipeline verifies that provenance against an exact expected identity before it deploys, and it deploys by digest only. The cluster refuses, or at least audits, images that fail verification. Signing that nobody verifies adds no security, so verification is the real deliverable.\n\n## When to apply\n- Adding a release or promote workflow that pushes images to ACR or GHCR, or publishes JARs or binaries\n- Asked to \"reach SLSA Build L2/L3\", \"sign our images\", or \"add provenance or SBOM attestations\"\n- Wiring a deploy job or GitOps step for AKS that should only accept images built by your own CI\n- Reviewing a workflow that uses `actions/attest`, `actions/attest-build-provenance`, cosign, or Notation\n- An audit asks where a running image came from and nobody can answer by digest\n\n## Baseline rules\n1. **Identify artifacts by digest, never by tag.** Attest, verify, promote, and deploy `registry/repo@sha256:...`. Tags are labels for people; a tag can be moved after it was verified. Take the digest from the build step's output (for example the `digest` output of `docker/build-push-action`), not from a second `docker pull`.\n2. **Attest in CI with short-lived identity, not stored keys.** On GitHub Actions use `actions/attest` (v4 covers provenance, SBOM, and custom predicates; the older `attest-build-provenance` and `attest-sbom` actions do the same jobs). Signing uses a short-lived Sigstore certificate tied to the workflow's OIDC token. No private signing keys in repository secrets. If policy requires Notation with your own certificate, keep the key in Azure Key Vault and sign through the Key Vault plugin using OIDC login, so the key never leaves the vault.\n3. **Least privilege per job.** Only the job that attests gets `id-token: write`, `attestations: write`, and `artifact-metadata: write`, plus registry write for pushing the attestation (`packages: write` for GHCR; an OIDC login with AcrPush for ACR). Build and test jobs stay read-only. Pin every action by commit SHA (see gha-devsecops-pipeline).\n4. **Attest what ships, not every build.** Attest release and promotion candidates, not every pull request test build. Never attest artifacts built from untrusted fork code or in `pull_request_target` jobs.\n5. **Know your SLSA level, and claim no more.** GitHub-hosted runners plus GitHub attestations give signed provenance from a hosted platform, which is roughly Build L2. For L3-style isolation, build and attest inside one reusable workflow that callers cannot change, and verify with `--signer-workflow` pointing at that reusable workflow. Self-hosted runners weaken the claim; verify with `--deny-self-hosted-runners` where you expect hosted builds.\n6. **SBOM from the built artifact.** Generate the SBOM from the final image or JAR (for example syft on the image, or the CycloneDX Maven plugin for the JAR), in SPDX or CycloneDX, and attest it to the same digest with `sbom-path`. Attach it to the release too. An SBOM made from the source tree alone misses what the base image added.\n7. **Verify with an exact identity before deploying.** The deploy job runs `gh attestation verify oci://@ --repo / --signer-workflow //.github/workflows/.yml --source-ref refs/heads/main` (or the release tag ref), and the SBOM check adds `--predicate-type` for your SBOM format. `--owner` alone is too loose for production. Fail closed: a verification error stops the deploy, with no retries and no bypass input.\n8. **Know which Sigstore instance signed it.** Public repositories sign with the Sigstore public-good instance, and the signatures go to a public transparency log, so provenance reveals repository and workflow names. Private and internal repositories (GitHub Enterprise Cloud) sign with GitHub's own Sigstore instance, which has no public log. Any verifier outside `gh` needs GitHub's trusted root (`gh attestation trusted-root`). For offline or air-gapped checks, download the bundle and verify with `--bundle` and `--custom-trusted-root`.\n9. **Keep attestations with the image.** Use `push-to-registry: true` so the attestation is stored next to the image as an OCI referrer. When promoting between registries (dev ACR to prod ACR), copy by digest with a tool that also copies referrers (for example `oras cp -r`), or verify in the source registry and then attest the promotion in the target. Don't rebuild on promote.\n10. **Enforce at admission, audit first.** On AKS, add an admission check that verifies signatures or attestations for your registries: Kyverno `verifyImages` with a keyless attestor whose subject and issuer match your workflow, or Ratify with Gatekeeper and a custom Azure Policy set to Deny. AKS Image Integrity is preview, supports Notation only, and can only audit, so use it for visibility rather than enforcement. Roll out in audit mode, fix the findings, then enforce namespace by namespace. Scope exemptions narrowly and review them (system namespaces, a break-glass label with an owner and an expiry), and set webhook timeouts and failure policy deliberately.\n11. **Verify what you consume.** Pin base images by digest, and verify the upstream signature or provenance when the publisher provides one. Record that check in the build log so it is auditable.\n12. **Provenance is not a vulnerability verdict.** Provenance tells you where an artifact came from, not whether it is safe. Keep CVE scanning, license policy, and code review as separate gates.\n\n## Workflow\n1. **Inventory.** List every shipped artifact, its registry or feed, the workflow that builds it, how deploys pick the version (tag or digest), and who consumes it.\n2. **Attest.** Add an attest job or step to the release workflow: provenance, plus an SBOM attestation for the same digest, both pushed to the registry. Scope permissions to that job.\n3. **Verify in the pipeline.** Add the exact-identity `gh attestation verify` step to the deploy or promote job, and pass the verified digest (not the tag) to the manifest or overlay.\n4. **Harden the builder.** If you need L3-style guarantees, move build and attest into a reusable workflow and pin callers to it.\n5. **Admission.** Deploy the cluster policy in audit mode, review violations for a week, then enforce on application namespaces.\n6. **Prove it.** Push an unsigned image and an image built by a different workflow to a test namespace. Both must fail verification in the pipeline and be flagged or blocked at admission.\n\n## Review checklist\n- [ ] Attest, verify, and deploy all use the image digest; no deploy references a mutable tag\n- [ ] Attestation uses OIDC-backed keyless signing or a Key Vault-held key; no signing keys in repository secrets\n- [ ] Only the attest job has `id-token`, `attestations`, and `artifact-metadata` write; all actions are SHA-pinned\n- [ ] No attestation from fork or `pull_request_target` code; PR test builds are not attested\n- [ ] An SBOM is generated from the built artifact and attested to the same digest\n- [ ] The deploy job verifies with `--repo` plus `--signer-workflow` (and `--source-ref` where it applies) and fails closed\n- [ ] Private-repo verifiers use GitHub's trusted root; offline verification is documented if needed\n- [ ] Attestations travel with promoted images (referrers copied) or are re-verified and re-attested\n- [ ] The admission policy is in audit or enforce with documented, expiring exemptions\n- [ ] Negative tests (an unsigned image, a wrong-workflow image) fail as expected\n\n## Copy-ready SKILL.md stub\n```markdown\n---\nname: artifact-provenance-signing-guardrails\ndescription: Use when adding or reviewing build provenance, SBOM attestations, or image signing for container images, JARs, or binaries built in GitHub Actions and deployed to AKS — digest-only identity, keyless OIDC attestation with actions/attest, least-privilege attest job, SBOM from the built artifact, exact-identity gh attestation verify before deploy (repo, signer workflow, source ref), private-repo trusted roots, attestations copied on promote, and admission verification rolled out audit-first.\n---\n\nFollow Build Provenance and Image Verification Guardrails: identify every artifact by digest and never deploy a tag. Attest release builds in CI with actions/attest using short-lived OIDC identity, not stored keys, and give only the attest job id-token, attestations, artifact-metadata, and registry write. Generate the SBOM from the built artifact and attest it to the same digest. Before deploying, run gh attestation verify with --repo, --signer-workflow, and --source-ref, and fail closed. Use a reusable build workflow for SLSA L3-style isolation. Copy attestations with promoted images. Enforce signature or attestation checks at AKS admission, audit first, with narrow, expiring exemptions. Treat provenance as origin proof, not a vulnerability verdict.\n```\n" } ] } From cbeb2746c8ea499ee4f64e285518a4aef57636aa Mon Sep 17 00:00:00 2001 From: Ramkumar Ethiraj <64294490+rmkr-dev@users.noreply.github.com> Date: Tue, 27 Oct 2026 05:07:56 +0000 Subject: [PATCH 2/2] Add build provenance and deploy-time verification prompt --- promptbook/prompts.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/promptbook/prompts.json b/promptbook/prompts.json index 39f11a0..46a1d70 100644 --- a/promptbook/prompts.json +++ b/promptbook/prompts.json @@ -799,6 +799,26 @@ ], "formatNotes": "All three. Point the tool at the manifests folder, the CI workflow that deploys it, and any per-environment values files. Copilot: kustomize path instructions plus those files in agent mode (or save it as `.github/prompts/scaffold-kustomize-aks-overlays.prompt.md`). Claude Code: the kustomize-aks-overlay-guardrails skill plus the files. Codex: AGENTS.md Kubernetes manifests section, then the diff.", "body": "Task: Set up or clean up Kustomize manifests for the service I specify so each environment is a small overlay on one base, every overlay renders and validates in CI, and reviewers see the rendered diff. Finish with a minimal diff, the CI job, and a rendered-output comparison.\n\nContext placeholders (fill from the repo or ask only if missing):\n- Manifests root: {{K8S_PATH}} (e.g. deploy/k8s)\n- Environments and clusters: {{ENVS}} (e.g. dev, stage, prod on AKS; namespace per env)\n- Kubernetes versions: {{K8S_VERSIONS}} (per cluster)\n- Image registry and tagging: {{IMAGES}} (e.g. ACR, digest pinned by CI)\n- Config and secrets: {{CONFIG}} (which values differ per env; where secrets live, e.g. Azure Key Vault)\n- Identity: {{IDENTITY}} (e.g. AKS workload identity client ID per env)\n- Optional pieces: {{COMPONENTS}} (e.g. monitoring annotations, PodDisruptionBudget, topology spread for prod)\n- CI system: {{CI}} (GitHub Actions | Azure Pipelines | Jenkins)\n- Tool versions: {{TOOLS}} (kustomize, kubeconform, kube-linter or policy tool)\n\nRequirements:\n\n1. Map the current state\n - Table: overlay | what it changes from base | cluster and namespace | deprecated fields | copied base resources | secrets in Git\n - Render every overlay as it is today with the pinned kustomize version and save the output for comparison\n\n2. Modernize\n - Replace `bases`, `commonLabels`, `patchesStrategicMerge`, `patchesJson6902`, and `vars` with `resources`, `labels`, `patches`, and `replacements` (`kustomize edit fix` first, then hand-check)\n - Rendered output must match the saved output, or list and justify every difference\n\n3. Structure\n - `base/` environment-neutral; `components/` for {{COMPONENTS}}; `overlays//` with only real differences\n - Strategic merge patches for values; JSON 6902 patches with explicit targets for list edits (`args`, `command`, env lists without a merge key) and removals\n - Small, named patch files; no overlay copies a full base resource\n\n4. Labels and selectors\n - Stable identity labels in base selectors; extra labels via `labels:` with `includeSelectors: false`\n - Flag any change that would alter an existing Deployment or StatefulSet selector and stop for confirmation instead of applying it\n\n5. Images and config\n - Images set with the `images:` field in each overlay; prod pinned to a digest or immutable tag; no `latest` in any rendered output\n - ConfigMaps from `configMapGenerator` with hash suffixes on; document any `disableNameSuffixHash`\n\n6. Secrets and identity\n - No secret values or committed secret files; use a SecretProviderClass for Azure Key Vault through the Secrets Store CSI driver with workload identity from {{IDENTITY}}\n - List any secret that was committed in history so it can be rotated\n\n7. CI gate in {{CI}}\n - For each overlay: build with pinned kustomize, fail on warnings; kubeconform strict against {{K8S_VERSIONS}} with CRD schemas; policy check with the pinned tool\n - On pull requests, render each overlay on the base and head branches and publish the diff as a comment or artifact\n - Pin every tool version and action\n\nConstraints:\n- Minimal diff. Don't change resource requests, replicas, probes, or app config values unless a finding requires it; report findings instead.\n- Don't apply anything to a cluster. `kubectl diff -k` against non-production is the most you suggest running.\n- Don't invent cluster names, versions, IDs, or digests; use placeholders and state assumptions.\n- Don't add Helm, and don't enable Helm inflation inside Kustomize.\n- Optimize for GitHub Copilot, Claude Code, and Codex: give concrete diffs and a checklist.\n\nOutput format:\n1. Current-state table with flagged issues\n2. New directory tree\n3. Patch (kustomization files, patches, components, generators, SecretProviderClass, CI job)\n4. Rendered-output comparison per overlay: identical, or each difference with its reason\n5. Risk list (selector changes, secrets to rotate, version gaps) and follow-ups\n\nStart from {{K8S_PATH}}. Render before you refactor, and prove the rendered output is unchanged unless you meant to change it.\n" + }, + { + "id": "add-build-provenance-and-image-verification", + "title": "Add Build Provenance, SBOM Attestations, and Deploy-Time Verification", + "whenToUse": "Add signed provenance and SBOM attestations to a GitHub Actions release pipeline, verify them with an exact repo, workflow, and ref identity before every AKS deploy, promote images by digest with their attestations, and stage admission enforcement audit-first, with negative tests that prove unsigned or foreign images are rejected.", + "tools": [ + "copilot", + "claude-code", + "codex" + ], + "tags": [ + "supply-chain", + "slsa", + "github-actions", + "aks", + "devsecops", + "security" + ], + "formatNotes": "All three. Point the tool at the release and deploy workflows, the Dockerfile, the manifests or overlays that reference the image, and any existing cluster policies. Copilot: supply-chain path instructions plus those files in agent mode (or save it as `.github/prompts/add-build-provenance-and-image-verification.prompt.md`). Claude Code: the artifact-provenance-signing-guardrails skill plus the files. Codex: AGENTS.md release and supply chain section, then the diff.", + "body": "Task: Add signed build provenance and SBOM attestations to the release pipeline for the artifacts I specify, verify them with an exact identity before every deploy, and stage admission enforcement on AKS. Finish with a minimal diff, a verification runbook, and negative tests.\n\nContext placeholders (fill from the repo or ask only if missing):\n- Artifacts: {{ARTIFACTS}} (e.g. container image app-api, Spring Boot JAR, CLI binary)\n- Registries: {{REGISTRIES}} (e.g. dev ACR and prod ACR, or GHCR)\n- Release workflow: {{RELEASE_WORKFLOW}} (e.g. .github/workflows/release.yml; trigger such as tag v* or push to main)\n- Deploy path: {{DEPLOY}} (e.g. GitHub Actions deploy job with Kustomize overlay, or GitOps repo)\n- Repo visibility and plan: {{VISIBILITY}} (public, or private on GitHub Enterprise Cloud)\n- Runners: {{RUNNERS}} (GitHub-hosted or self-hosted)\n- Target SLSA build level: {{SLSA_LEVEL}} (L2 or L3)\n- SBOM format and tool: {{SBOM}} (e.g. CycloneDX via syft, or the CycloneDX Maven plugin)\n- Admission option: {{ADMISSION}} (Kyverno verifyImages, Ratify with Gatekeeper and Azure Policy, or none yet)\n- Signing requirement: {{SIGNING}} (keyless via actions/attest, or Notation with a Key Vault certificate)\n\nRequirements:\n\n1. Map the current state\n - Table: artifact | built by (workflow/job) | pushed to | how deploy selects it (tag or digest) | signed or attested today | verified where\n - Flag mutable-tag deploys, stored signing keys, broad workflow permissions, unpinned actions, and attestations from untrusted triggers\n\n2. Attest\n - Capture the digest from the build step output; attest `name@digest`, never a tag\n - Add `actions/attest` (SHA-pinned) for provenance and a second call with `sbom-path` for the SBOM, both with `push-to-registry: true`\n - Job-level permissions only on the attest job: `id-token: write`, `attestations: write`, `artifact-metadata: write`, plus registry write (`packages: write` for GHCR, or OIDC login with AcrPush for ACR)\n - No attestation on pull_request, fork, or `pull_request_target` runs\n - If {{SIGNING}} is Notation: sign through the Azure Key Vault plugin after OIDC login; no key material in secrets\n\n3. Builder isolation for {{SLSA_LEVEL}}\n - L2: GitHub-hosted runners and the steps above\n - L3: move build and attest into a reusable workflow that callers can't modify; callers pass inputs only\n - Say plainly which level the result supports and why; don't overclaim with self-hosted runners\n\n4. Verify before deploy\n - In {{DEPLOY}}: `gh attestation verify oci://@ --repo / --signer-workflow --source-ref `; add `--deny-self-hosted-runners` when builds are hosted; verify the SBOM attestation with `--predicate-type`\n - Fail closed: no retry loop, no skip input, no `continue-on-error`\n - Pass the verified digest into the manifest or overlay; the deployed spec must reference the digest\n - Private repos: document the trusted-root step for any verifier outside gh, and offline verification with `--bundle` if needed\n\n5. Promotion\n - Promote by digest without rebuilding; copy referrers with the image (for example `oras cp -r`), or verify in the source and re-attest in the target\n - Re-verify in the target registry before the prod deploy\n\n6. Admission on AKS ({{ADMISSION}})\n - Policy that checks signatures or attestations for {{REGISTRIES}} with an identity matching the release workflow\n - Ship in audit mode first; list how to read violations; enforce on app namespaces only after a clean audit window\n - Narrow, documented exemptions with owner and expiry; explicit webhook timeout and failure policy\n - Note that AKS Image Integrity is preview, Notation-only, and audit-only, if it is under consideration\n\n7. Negative tests\n - An unsigned image, an image attested by a different workflow, and a tag swapped after verification: each must fail in the pipeline and be flagged or blocked at admission\n\nConstraints:\n- Minimal diff. Don't change build steps, tests, scanners, or app code beyond what attestation and verification need.\n- Never add signing keys, tokens, or certificates to the repo or to plain secrets; never print OIDC tokens.\n- Don't apply admission policies to a cluster. Provide the manifests and the commands for a non-production dry run.\n- Don't invent registry names, workflow paths, digests, or tenant IDs; use placeholders and state assumptions.\n- Treat provenance as origin proof only; keep CVE and license gates as they are.\n- Optimize for GitHub Copilot, Claude Code, and Codex: give concrete diffs and a checklist.\n\nOutput format:\n1. Current-state table with flagged issues\n2. Patch (release workflow, reusable workflow if L3, deploy verification step, promotion step, admission policy in audit mode)\n3. Permission matrix per job\n4. Verification runbook: exact commands for a developer, for CI, and for an incident responder checking a running pod's image\n5. Negative test plan and expected results\n6. Risks, SLSA level actually achieved, and follow-ups\n\nStart from {{RELEASE_WORKFLOW}}. Capture the digest first, verify before you deploy, and audit before you enforce.\n" } ] }