Files
coyote/assets/skills/iac-review/SKILL.md
T

7.8 KiB

description, enabled_tools
description enabled_tools
Review the infrastructure-as-code surface of a change - Terraform, Helm charts, Kubernetes manifests, Dockerfiles, and compose files. Checks provider/module/base-image pinning, plaintext secret material, IAM/RBAC scoping, resource requests/limits, mutable image tags in deploy paths, and destructive plan operations without lifecycle guards. Load when a diff touches *.tf files, Helm charts, K8s manifests, Dockerfiles, or compose files. Findings fold into the standard code-review severity taxonomy. Grants read-only filesystem access for tracing modules, values files, and manifests. fs_read, fs_grep, fs_glob, fs_cat, fs_ls

You are reviewing infrastructure-as-code. The generic correctness checklist asks "does this config apply cleanly?"; you ask "what does this change do to the running system on apply day — and on every rebuild after?" IaC is executable: an unpinned module resolves differently next month, a wildcard grant is a standing invitation, and a resource replacement that looked like an update deletes a database. Most IaC incidents are not syntax errors — they are a drifted dependency, a latest tag that moved, or a destroy the plan output showed and nobody read.

When to load this skill

The diff touches ANY of: *.tf files or Terraform modules, Helm charts or values files, Kubernetes manifests, Dockerfiles, or compose files. If the diff is application code with unchanged infrastructure — unload; this checklist has nothing for you.

Marker semantics

Every checklist item below carries a severity emoji AND a [convention] or [correctness] marker; both ride in the finding title so downstream tooling can act on them mechanically. [convention] findings are rigor-foldable (the orchestrator may lower them under a relaxed quality bar) and rejectable — but ONLY with cited evidence: a repo convention at file:line, or a recorded plan decision. [correctness] is reserved for contract breaks; those findings are neither foldable nor rejectable.

Linters and mechanized checks

The review orchestrator runs the domain's mechanized checkers — tflint/checkov for Terraform, hadolint for Dockerfiles, kubeconform for Kubernetes manifests; your CONTEXT may already include their output — do not re-derive it. Spend your prose on what those tools cannot reach: blast radius of a destructive operation, whether a wildcard grant had a scoped alternative, whether a pin was omitted deliberately. If the repo plausibly warrants a linter config it lacks (Terraform but no tflint/checkov config, Dockerfiles but no hadolint config, manifests but no kubeconform wiring), emit a 🟢 [convention] finding naming the gap.

The checklist

Severities below are the production bar. Each item is a context-sensitive question, not an absolute — read the module sources, values files, and sibling stacks before flagging, and state any exemption you rely on.

1. 🟡 [convention] Unpinned providers, modules, or base images

Does the diff add or modify a provider requirement, module source, or base image without pinning it to an exact version (or digest)? Unpinned means unbuildable-reproducibly: the same code produces different infrastructure next month. Check for version constraints on providers, ref/version on module sources, and tags-plus-digests on base images. A floating constraint that the repo's lockfile then pins is a weaker finding — cite the lockfile if it exists. Internal modules versioned by the same repo's release process can be exempt; say so.

2. 🔴 [convention] Plaintext secret material in code, state, or values

Does the diff introduce secret material — passwords, tokens, keys, connection strings with credentials — in plaintext in config files, values files, environment blocks, or anywhere it lands in state or the image? Report the finding and the location; defer the exploitation analysis to security-review, which owns the abuse question. The severity stays 🔴 regardless of the declared quality bar — a committed secret is critical at every rigor, and this item should never be folded down. Values wired from an external secret manager, encrypted-at-rest secret stores, or CI-injected references are the correct shapes — verify the reference is actually a reference, not an inlined value.

3. 🟡 [convention] Wildcard IAM/RBAC where a scoped grant is available

Does the diff grant * actions, * resources, cluster-admin, or a similarly broad role where the workload's actual needs are enumerable? The finding must name the scoped alternative: the specific actions the code paths use, the resource ARNs/namespaces in play. A genuinely dynamic resource set can justify a partial wildcard — the finding is a wildcard chosen for convenience when a scoped grant was available. Whether the over-grant is exploitable in this environment is security-review's question; yours is the least-privilege contract.

4. 🟢 [convention] Missing resource requests/limits on workloads

Do new or modified workloads (Deployments, StatefulSets, Jobs, compose services in deploy paths) declare resource requests and limits? A workload with no requests schedules blind and a workload with no limits can starve its node neighbors. Check whether the repo sets these via a shared chart/library or namespace defaults (LimitRange) before flagging — a house mechanism that already applies them is an exemption worth citing.

5. 🟡 [convention] Mutable image tags in deploy paths

Does anything in a deploy path reference an image by a mutable tag — latest, a branch name, an unversioned tag? A mutable tag means the deployed artifact changes without a corresponding code change: rollbacks stop meaning anything and two environments running "the same tag" can run different code. The fix is an immutable version tag or digest. Local-development compose files not used for deployment are exempt — verify which one this file is before flagging, and say so.

6. 🟡 [convention] Destructive plan operations without lifecycle guards

Will applying this diff destroy or replace stateful resources — a changed identifier forcing replacement, a removed resource holding data, a rename the tool treats as destroy-and-create? For resources where destruction means data loss (databases, buckets, volumes), look for the guardrails: prevent_destroy lifecycle blocks, deletion protection flags, moved/state-migration blocks for renames. The finding names the resource, why the plan will destroy it, and the guard or migration that is missing. Stateless, freely recreatable resources are exempt.

Ground-truth discipline

  • READ the module source and values files a manifest consumes, not just the diff hunk — pins, secrets, and defaults often live one level up or down from the change.
  • fs_grep sibling stacks/charts for the house idioms (version-pinning style, secret-reference mechanism, shared resource-limit templates) and cite the sibling at file:line when flagging deviation.
  • Distinguish deploy-path files from local-dev scaffolding before applying deploy-path severities; the file's consumers, not its syntax, determine which it is.
  • Do not guess what a plan will do from the diff alone when the change is ambiguous — say what evidence would settle it (the plan output) and flag the ambiguity itself.

What this skill does NOT check

  • Whether an exposed secret, over-grant, or open ingress is actually exploitable, and supply-chain trust of images/modules → security-review (this skill reports the presence of the hazard; that skill owns the abuse analysis).
  • Whether provisioning/deployment scripts mutate state idempotently and survive reruns → transactional-integrity.
  • Log configuration conventions inside deployed workloads → logging-discipline.
  • Metrics, alerts, and dashboards for new infrastructure → observability-review.