skip to content

When you run `kics scan -p . -o ./kics-out` against a repository, what does KICS scan and which report files does it write?

level: juniorimportance: must knowfreq 22%

answer

  1. detect first, then load queries
  2. query.rego beside metadata.json
  3. no -o, no file
  4. json and results are the defaults

basics

~10 s

KICS detects which supported IaC types the path contains, runs the matching built-in Rego queries, prints the findings, and writes results.json into ./kics-out; other formats need --report-formats, and without -o no file is written.

solid answer

~40 s

`kics scan` walks every path given to `-p` (a directory, a file, an archive, or a `git::` or `s3::` source), and a pre-scan analysis decides which platforms are present — Terraform `.tf`, CloudFormation, Kubernetes manifests, Ansible playbooks, Dockerfiles, OpenAPI documents and more — so only the matching queries load. Each built-in query is a `query.rego` paired with a `metadata.json` carrying its UUID, severity and category. Findings are printed to the console; a report file appears only when `-o, --output-path` names a directory. `--report-formats` defaults to `json` and `--output-name` to `results`, so this command leaves `./kics-out/results.json`; add `--report-formats json,sarif` or `all` for more. By default KICS also skips paths matched by the root `.gitignore`, and by default a scan that finds results exits with a non-zero, severity-coded status.

go deeper

for a junior

Recall the command shape: kics scan, -p for what to scan, -o for where reports go, json as the default format, and that a scan with findings exits non-zero.

for a middle

Explain the detect-then-load flow, how query.rego and metadata.json pair up, and which defaults quietly shrink coverage: the root .gitignore and the 5 MB file cap.

for a senior

Show that you read files_scanned against files_parsed and queries_failed_to_execute before trusting a clean report from a repository that mixes several IaC technologies.

for a principal

Weigh one scanner across Terraform, Ansible, Kubernetes and OpenAPI against a tool per technology: one report shape and one gate against depth on each platform.

## What `kics scan` actually does **KICS** (Keeping Infrastructure as Code Secure) is Checkmarx's open-source static scanner for infrastructure-as-code. It never talks to a cloud account: it reads files, turns each one into a JSON document, and evaluates a library of **queries** against those documents. The entry point is the `scan` subcommand — since KICS 1.3 the binary no longer scans by default, so `kics scan` must be named. A run has four stages: 1. **Collect.** Every path given to `-p, --path` is walked. A path may be a local file or directory, an archive (`zip`, `tar.gz` and similar), or a remote source in go-getter syntax such as `git::https://...` or `s3::...`; remote sources are fetched into a temporary folder first. 2. **Detect.** A pre-scan analysis decides which **platform** each file belongs to, so only the parsers and queries for platforms actually present are loaded. `-t, --type` narrows this by hand, and `kics list-platforms` prints the accepted names. 3. **Evaluate.** Each built-in query is a folder holding a `query.rego` (the Rego logic) and a `metadata.json` (its `id` UUID, `queryName`, `severity`, `category` and `platform`). The library ships under `assets/queries`, organised by platform and, where it applies, by cloud provider. 4. **Report.** Findings are printed to the console and, when asked, written as report files. ## Which platforms one scan covers A repository that mixes several technologies needs one scan, not several: | Platform (`--type` value) | What KICS reads | |---|---| | `Terraform` | `.tf` files, with variables from `terraform.tfvars` and `*.auto.tfvars` in the same directory | | `CloudFormation` | templates in `.json` or `.yaml`, AWS SAM files included | | `Kubernetes` | manifests in YAML | | `Ansible` | playbooks, plus Ansible config and inventory files | | `Dockerfile` / `DockerCompose` | build files and compose files | | `OpenAPI` | Swagger 2.0 and OpenAPI 3.0 documents in JSON or YAML | Others include `AzureResourceManager`, `GoogleDeploymentManager`, `Pulumi`, `Crossplane`, `Knative`, `ServerlessFW`, `Buildah`, `GRPC` and `CICD`. Two platform details matter on day one: KICS decrypts Ansible Vault files on the fly when the `ANSIBLE_VAULT_PASSWORD_FILE` environment variable is set, and it does not follow `$ref` links between OpenAPI files unless `--enable-openapi-refs` is passed. ## Where the findings go | Flag | Default | Effect | |---|---|---| | `-o, --output-path` | none | directory for report files; without it, **no file is written** | | `--report-formats` | `json` | any of `json`, `sarif`, `html`, `junit`, `csv`, `cyclonedx`, `glsast`, `sonarqube`, `asff`, `codeclimate`, `pdf`, or `all` | | `--output-name` | `results` | base name for the report files | So `kics scan -p . -o ./kics-out` leaves exactly one file, `./kics-out/results.json`. Adding `--report-formats json,sarif` also writes `results.sarif`, and some formats add a prefix: the GitLab SAST report is named `gl-sast-results.json`. ## What the JSON report contains The JSON report is the one to read first, because it carries the scan's own bookkeeping as well as the findings: - `files_scanned` and `files_parsed` — files that reached the parser and files that parsed; a gap means files KICS could not read. - `queries_total` and `queries_failed_to_execute` — queries loaded and queries that did not finish. - `severity_counters` and `total_counter` — counts per severity (`CRITICAL`, `HIGH`, `MEDIUM`, `LOW`, `INFO`, `TRACE`). - `queries[]` — one entry per query that matched, with `query_id` (the metadata UUID), `query_name`, `severity`, `platform` and `category`. - `queries[].files[]` — each location, with `file_name`, `line`, `expected_value`, `actual_value` and a `similarity_id` hash identifying that single finding. ## Defaults that surprise first-time users - **No `-o`, no artefact.** `--report-formats sarif` without `-o` writes nothing, and a CI step that uploads `results.sarif` then finds no file. - **`.gitignore` is honoured.** Paths matched by the `.gitignore` at the root of the scanned path are excluded, and the log says so; `--exclude-gitignore` turns that off. - **Large files are skipped.** `--max-file-size` defaults to 5 MB, and a larger file produces only a warning in the log. - **The exit status carries meaning.** By default a run that finds results exits non-zero with a severity-coded status, so a first CI run "fails" even though the scan itself worked. The official container image is the usual way to start, because it carries the query library at the default path: ```bash docker run -t -v "$PWD":/path checkmarx/kics scan -p /path -o /path/kics-out --report-formats json,sarif ```

  • How do you scan only the Terraform and Kubernetes parts of a repository that also holds Ansible and OpenAPI files?
    Pass `-t Terraform,Kubernetes` (the `--type` list is case-insensitive) so only those platforms' parsers and queries load, or the inverse with `--exclude-type Ansible,OpenAPI`; the two flags cannot be combined. `-e, --exclude-paths` with globs skips directories instead of platforms. Anything outside the selection is simply not scanned, and the report does not list what was left out.
  • Can KICS scan a repository without cloning it first?
    Yes. `-p` accepts go-getter sources, so `-p "git::https://git.example.internal/platform/infra.git"` fetches the repository into a temporary folder, scans it and removes the folder afterwards. `s3::` and `gcs::` sources and archives such as `.zip` or `.tar.gz` work the same way. Access comes from an SSH key, the environment or the source URL's own parameters, not from a KICS credential flag.

saying these in an interview costs you the question

  • KICS logs into the cloud account to check the deployed resources.
  • --report-formats on its own writes report files to the current directory.
  • You must pass --type for every platform the repository contains.
  • A non-zero exit from kics scan means the scanner crashed.
  • KICS only reads Terraform; Ansible and Kubernetes need another scanner.