Infrastructure-as-Code security scan for changed deploy config — Dockerfiles,
docker-compose, Terraform, Kubernetes/Helm, and CI/CD workflows. Flags
misconfigurations and exposed secrets (root containers, open ingress, wildcard
IAM, privileged pods, untrusted CI triggers). Static diff-scoped analysis (safe
to auto-run); uses tfsec/checkov/hadolint/actionlint when present. Use when asked
to "scan infra", "IaC scan", "check the Dockerfile/terraform/k8s", "CI security",
or after changing deploy/...
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
SSkills DirectorySkills Directory
Is that skill safe to install?
Are you the author of Iac Scan?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/bg-szy-iac-scan)
---
name: iac-scan
version: 1.0.0
description: |
Infrastructure-as-Code security scan for changed deploy config — Dockerfiles,
docker-compose, Terraform, Kubernetes/Helm, and CI/CD workflows. Flags
misconfigurations and exposed secrets (root containers, open ingress, wildcard
IAM, privileged pods, untrusted CI triggers). Static diff-scoped analysis (safe
to auto-run); uses tfsec/checkov/hadolint/actionlint when present. Use when asked
to "scan infra", "IaC scan", "check the Dockerfile/terraform/k8s", "CI security",
or after changing deploy/infra config.
triggers:
- iac scan
- scan infra
- infrastructure scan
- check the dockerfile
- terraform security
- kubernetes security
- ci security review
argument-hint: '<optional: paths/globs to scope (defaults to changed infra files)>'
allowed-tools:
- Bash
- Read
- Grep
- Glob
- Task
- WebFetch
---
# /iac-scan
Scan changed infrastructure/deploy config for security misconfigurations and
exposed secrets. **Static and read-only** — analyzes the files directly.
**Auto-run safety envelope:** the auto-run path uses only **local static linters**
that need no network and no auth — `hadolint`, `tfsec`, `checkov` (offline/local
policy only), `kube-score`, `actionlint`, `zizmor`. That's why it's safe to
auto-run, unlike `/pentest` (an authenticated, networked scanner that probes live
targets). Anything that reaches the network — e.g. `trivy` pulling vulnerability
DBs, or `checkov` fetching remote policies — is **opt-in / recommend-only**, never
part of the auto-run. If only networked tools are available, run the signature
checks instead and recommend the networked scan separately.
## Hard rules
- **Evidence-grounded:** file:line, the offending directive, the rule/CWE, and the
concrete risk ("container runs as root", "0.0.0.0/0 on port 22", "secret baked
into image layer").
- **Severity by blast radius:** internet-exposed, privilege-escalating, or
secret-leaking misconfigs are CRITICAL/HIGH; style/pinning nits are LOW.
- **Smallest correct fix**, shown as a diff; do not apply it.
- **Scope to the changed infra files.** Don't re-scan the whole tree.
- **Prefer real scanners when present.** Local static linters (`hadolint`,
`tfsec`, `kube-score`, `actionlint`, `zizmor`, offline `checkov`) are part of
the auto-run path; networked scanners (`trivy` DB pulls, remote-policy `checkov`)
are recommend-only. Fall back to the signature checks below when none are
present — and always say which mode ran (which tools, or signature mode).
## Trigger (what counts as infra/deploy config)
- Containers: `Dockerfile`, `*.dockerfile`, `docker-compose*.y*ml`, `.dockerignore`
- Terraform: `*.tf`, `*.tfvars`
- Kubernetes / Helm: `*.y*ml` with `kind:` under `k8s/`, `manifests/`, `charts/`,
`helm/`, or `deploy/`
- CI/CD: `.github/workflows/**`, `.gitlab-ci.yml`, `Jenkinsfile`, `*.circleci/*`
- Web/proxy/cloud: `nginx.conf`, `Caddyfile`, `*.tf` cloud resources, `serverless.yml`
If none of these changed, say so and stop.
## What to check
**Docker** — non-root `USER`; pinned base image (no `:latest`); no secrets in
`ENV`/`ARG`/layers; no `ADD http://…`; minimal surface; `HEALTHCHECK`; no
unnecessary `EXPOSE`; `--no-install-recommends`/cleaned caches. *(hadolint)*
**docker-compose** — no `privileged: true`; no `network_mode: host`; never bind
`/var/run/docker.sock`; secrets via `secrets:` not `environment:`; pinned image tags.
**Terraform** — no security-group/firewall `0.0.0.0/0` on sensitive ports
(22/3389/DB); no public buckets/blobs; no IAM `Action: "*"` / `Resource: "*"`;
encryption at rest + in transit enabled; no hardcoded secrets/keys; remote state
with locking; logging/versioning on. *(tfsec / checkov)*
**Kubernetes / Helm** — `runAsNonRoot`, drop `privileged`/`allowPrivilegeEscalation`;
no `hostPath`/`hostNetwork`/`hostPID`; resource `limits`/`requests` set;
`readOnlyRootFilesystem`; no `default` ServiceAccount with broad RBAC / `cluster-admin`;
pinned image digests; NetworkPolicy present; no secrets in plain env. *(kube-score / kubesec)*
**CI/CD (GitHub Actions et al.)** — no `pull_request_target` running untrusted PR
code; least-privilege top-level `permissions:`; third-party actions pinned to a
**full commit SHA** (not a moving tag); no script injection via unsanitized
`${{ github.event.* }}` in `run:`; secrets never `echo`ed; no `persist-credentials`
left on when not needed. *(actionlint + zizmor)*
**Web/proxy** — TLS enforced; security headers (HSTS, CSP, X-Content-Type-Options);
no directory listing; no server tokens leaking versions.
## Steps
1. **Scope** — resolve changed infra files (or `$ARGUMENTS`). None → stop.
2. **Tool pass** — for each file type, if the matching scanner is installed, run it
scoped to the changed files and capture findings verbatim. Note which tools ran.
3. **Signature pass** — for file types with no installed scanner, Grep/Read for the
patterns above. Mark these **Observed** (clear match) vs **Suspected**.
4. **Secrets sweep** — across all changed infra files, flag high-entropy strings,
`AKIA…`, private keys, tokens, `password=`/`secret=` literals. (Complements
`/defense`'s app-code secret scan.)
5. **Report** — group by severity; smallest fix per finding as a diff.
## Report format
```markdown
# IaC scan — <scope> @ <date>
Scanners run: hadolint ✓ / tfsec ✗ (signature mode) / actionlint ✓ …
## CRITICAL / HIGH
- <file:line> — <directive> — <rule/CWE> — <risk> — <fix>
## MEDIUM / LOW
- …
## Secrets
- <file:line> — <kind> — rotate + move to a secret manager
```
## Anti-patterns (do not do)
- Claiming a misconfig without quoting the directive and naming the risk.
- Scanning the whole repo instead of the changed infra files.
- Applying fixes or rewriting manifests — this scans and drafts; the user applies.
- Treating a pinning/style nit as CRITICAL, or burying an open-to-the-world port.
## Related commands
- `/defense` — app-code OWASP/secrets sweep; `/iac-scan` is its infra counterpart.
- `/pentest` — deeper external scanner; recommend after `/iac-scan` finds HIGH/CRITICAL
or when a live environment needs probing.
- `/cso` — threat-model the deployment topology at design time (`/write-plan`).