kics scan: Query-Based IaC Scanning in CI
kics scan queries IaC for security and misconfiguration issues across many platforms, with --fail-on controlling which severities break the build.
KICS (Keeping Infrastructure as Code Secure) scans Terraform, Kubernetes manifests, Helm, Dockerfiles, CloudFormation, and Ansible using a large open query set. In CI you point it at a path and choose which severities should fail the gate.
What it does
kics scan walks the path in -p, detects platforms, runs its queries, and reports results grouped by severity (HIGH, MEDIUM, LOW, INFO). By default it exits non-zero when results meet the --fail-on threshold. Reports can be written in several formats for archiving or code scanning.
Common usage
# scan a path and write JSON + SARIF reports
kics scan -p ./infra -o ./results --report-formats json,sarif
# only fail the build on HIGH severity findings
kics scan -p ./infra --fail-on high
# exclude a query and a path
kics scan -p ./infra --exclude-queries <query-id> --exclude-paths 'vendor/**'Options
| Flag | What it does |
|---|---|
| -p, --path <path> | File or directory to scan; repeatable |
| -o, --output-path <dir> | Directory to write report files |
| --report-formats <list> | json, sarif, html, junit, gitlab-sast, sonarqube |
| --fail-on <severities> | Severities that cause a non-zero exit, e.g. high,medium |
| --exclude-queries <ids> | Skip specific query ids |
| --exclude-paths <globs> | Skip files/dirs matching the globs |
In CI
Start with --fail-on high so the gate blocks only serious issues, then tighten. Write --report-formats sarif into the output dir and upload it for code scanning. To run offline, use the KICS container image or vendor the queries; --exclude-paths keeps third-party modules from creating noise.
Common errors in CI
The summary prints counts by severity ("HIGH: 2, MEDIUM: 5, ...") and exits non-zero when the --fail-on set is met (the CLI uses distinct exit codes per severity bucket). "No files were scanned" means -p matched nothing or everything was excluded. "unable to open queries dir" means the query path is missing; use the official image or point at the queries directory.
Using this in CI
A runner has no kubeconfig, no cached context, and no interactive auth. Every kubectl invocation in CI needs the context supplied explicitly, and most confusing CI failures here are the command running against the wrong cluster or no cluster at all.
# never rely on the ambient context on a runner
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get pods
# confirm what you are actually connected to before mutating anything
kubectl config current-context
kubectl cluster-info
# fail fast instead of hanging on an unreachable API server
kubectl --request-timeout=30s get nodes