skip to content

CI Integration and Output

In CI, --exit-code and --severity decide what fails the build, .trivyignore what is waived, SARIF where results go. Interviewers probe it because a scanner that blocks every build is switched off.

on this pageshow

explore

questions

5

Does a Trivy 0.74 scan fail a CI job by default when it finds a CRITICAL vulnerability, and what makes it fail?

level: juniorimportance: must knowfreq 42%

answer

  1. the default exit status
  2. a flag that names the code
  3. filters run before the decision
  4. secrets count too, not just CVEs

basics

~10 s

No. Trivy 0.74 exits 0 whatever it finds; setting --exit-code to a non-zero value makes it exit with that code when any finding survives filtering, and --severity HIGH,CRITICAL decides which findings survive.

solid answer

~40 s

By default `--exit-code` is `0`, so a scan that reports a hundred CRITICALs still exits 0 and the job goes green. Set `--exit-code 1` (or any non-zero code) and Trivy exits with it when anything is left after filtering: a vulnerability, a FAIL misconfiguration, a secret or a license finding. `--severity HIGH,CRITICAL` narrows what is left, and it filters the report too, so the MEDIUMs vanish from the output as well as from the gate. Ignore files, `--ignore-unfixed`, an `--ignore-policy` and VEX all run before the decision. `--exit-on-eol` is a separate gate for an end-of-life base OS and is checked first. Because Trivy also exits 1 on a fatal error such as a failed database download, a distinct code like `--exit-code 2` lets the pipeline tell findings from a broken scan.

code

bash · 9 lines
bash
status=0
trivy image --severity HIGH,CRITICAL --exit-code 2 --exit-on-eol 3 \
  registry.example.internal/shop/api:1.4.2 || status=$?
case "$status" in
  0) echo "gate passed" ;;
  2) echo "HIGH or CRITICAL findings remain"; exit 1 ;;
  3) echo "base OS is end-of-life"; exit 1 ;;
  *) echo "trivy itself failed (exit $status)"; exit 1 ;;
esac

go deeper

for a junior

Remember that Trivy exits 0 by default and only fails a job when --exit-code is non-zero, and that --severity HIGH,CRITICAL limits what can fail it.

for a middle

Explain the filter order behind the decision (severity, status, ignore file, Rego, VEX) and that secrets, FAIL misconfigurations and licenses count alongside vulnerabilities.

for a senior

Show you keep the gate trusted: a distinct exit code so a failed database download is not mistaken for findings, --exit-on-eol as its own signal, and a separate full report because --severity trims the output.

for a principal

Frame the gate as something teams must keep switched on: which finding classes fail it, how waivers are scoped, and how one checked-in trivy.yaml keeps local and CI runs identical across repositories.

## The default: Trivy reports, it does not judge A **Trivy** scan has two outputs: the report (table, JSON, SARIF and so on) and the process **exit status**. A CI system only looks at the exit status. In Trivy 0.74 the `--exit-code` flag defaults to `0`, so the scan exits 0 whether it found nothing or a hundred CRITICAL vulnerabilities; a job that runs `trivy image` with no gate flags always goes green, and the findings are information for whoever reads the log. Failing the job is an explicit opt-in, and how narrowly you opt in decides whether teams keep the gate: a gate that fires on every LOW in a base image is the one that gets commented out. ## What `--exit-code` counts Setting `--exit-code` to a non-zero value means "if any finding remains after filtering, exit with this code". Trivy evaluates the final, already-filtered result set, and the check is broader than vulnerabilities: | Finding class | Counts toward `--exit-code` when | |---|---| | Vulnerability | any remains after filtering | | Misconfiguration | its status is FAIL; passed checks never count | | Secret | any remains after filtering | | License | any remains, which needs the license scanner enabled | The default `--scanners` value is `vuln,secret`, so a plain `trivy image --exit-code 1` fails on a hard-coded access key as readily as on a CVE. That surprises teams who think of Trivy as only a CVE scanner. ## What runs before the decision The exit decision happens after the filters, which Trivy applies in this order: 1. **Severity**: `--severity` (default: all five of `UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL`) keeps only the listed levels. Which severity a finding carries, and which source it came from, is the vulnerability-database topic; here it is simply the value Trivy assigned. 2. **Status**: for example `--ignore-unfixed`, which drops vulnerabilities that have no fixed version yet. 3. **Ignore file**: IDs listed in `.trivyignore`, or in a YAML ignore file named with `--ignorefile`. 4. **Rego**: an `--ignore-policy` file. 5. **VEX**: statements supplied with `--vex`. Two consequences matter in CI: - `--severity` filters the **report** as well as the gate. A run with `--severity HIGH,CRITICAL` writes a report with no MEDIUM or LOW rows at all, so a team that also wants the full picture runs a second, non-gating scan or keeps a full JSON report. - A filtered or waived finding cannot fail the build. That is the point of the filters, and it is why the waivers themselves deserve review. ## `--exit-on-eol`: a separate gate `--exit-on-eol` takes its own code and fires when Trivy detects that the scanned operating system has reached end of service or life. It is checked **before** `--exit-code`, so an end-of-life base image returns the EOL code even when vulnerabilities also remain. Giving each gate its own number lets the pipeline report which rule tripped. ## Telling findings from a broken scan When Trivy itself fails (a rate-limited database download, an image it cannot pull, a config file it cannot parse) it logs a fatal error and exits **1**. If the gate also uses `--exit-code 1`, a red job could mean "HIGH findings" or "the scanner never ran", and people learn to press re-run until it goes green. A distinct code such as `2` keeps the two apart; both still fail the job, which is the safe direction. Keeping Trivy's database in a CI cache, which the official GitHub Action does by default, cuts down those download failures; mirrors and offline database handling are a separate topic. ## Keeping the gate in one place Trivy reads `trivy.yaml` from the current directory by default, or another file named with `--config`. The gate settings are top-level keys there (`exit-code`, `exit-on-eol`, `severity`), so a developer's local run and the CI run behave the same. Precedence runs CLI flag, then `TRIVY_*` environment variable, then the config file, then the built-in default. ## Mistakes that get a gate switched off - Gating on every severity of a fresh base image, so the first run fails on dozens of LOW findings nobody can fix this sprint. - Reusing exit code 1, so a rate-limited database download looks like a vulnerability and gets re-run until green. - Forgetting that secrets are scanned by default, then discovering the gate on a test fixture that holds a fake key. - Setting the gate in a wrapper's environment and in `trivy.yaml` with different values, so local runs and CI disagree. A minimal gate on one line: ```bash trivy image --severity HIGH,CRITICAL --exit-code 2 registry.example.internal/shop/api:1.4.2 ```

  • Why might a team run Trivy twice in the same CI job?
    Because `--severity` filters the report as well as the gate. A run with `--severity HIGH,CRITICAL --exit-code 1` writes a report with the MEDIUM and LOW findings missing. Teams that want a complete record run once with `--exit-code 0` and every severity to produce a JSON or SARIF report, then once more with the gate flags; the second run reuses the cached database.
  • Where should the gate's flags live so a developer's local Trivy run matches CI?
    In a checked-in `trivy.yaml`, which Trivy reads from the current directory by default; `exit-code`, `exit-on-eol` and `severity` are top-level keys. A CLI flag or a `TRIVY_*` environment variable still overrides the file, so a CI wrapper that sets either one silently changes the gate. An explicit `--config` path that does not exist stops Trivy with an error.
  • Does `--exit-code 1` fail a `trivy image` run that found only a hard-coded secret?
    Yes. The default `--scanners` value in Trivy 0.74 is `vuln,secret`, and any secret finding that survives filtering counts toward the exit code exactly like a vulnerability. Teams that only want CVEs to gate either pass `--scanners vuln` or waive the specific secret rule, but a real leaked key is usually the finding most worth failing on.

saying these in an interview costs you the question

  • Trivy fails the build by default whenever it finds a CRITICAL vulnerability.
  • --severity only changes the exit decision; the report still lists every severity.
  • --exit-code only reacts to vulnerabilities, never to secrets or failed misconfiguration checks.
  • Exit code 1 from Trivy always means vulnerabilities were found.
  • A passed misconfiguration check counts toward the exit code.
open as a page

In Trivy 0.74, how do .trivyignore and .trivyignore.yaml differ when you must waive one CVE for one package until a date?

level: middleimportance: should knowfreq 28%

basics

~20 s

A .trivyignore line waives an ID for every package, file and scanner, optionally until an exp: date. The experimental .trivyignore.yaml, loaded only through --ignorefile, scopes the waiver by purls or paths and adds expired_at and a statement.

open as a page

A Trivy 0.74 pipeline must feed a JUnit test report, a code-scanning dashboard and a dependency graph — which --format does each need?

level: middleimportance: should knowfreq 21%

basics

~20 s

JUnit needs --format template with --template @contrib/junit.tpl, the dashboard needs --format sarif, and the dependency graph needs --format github, a dependency snapshot rather than a findings list. Keep a --format json report as the complete record.

open as a page

A trivy-action step sets severity HIGH,CRITICAL, exit-code 1 and format sarif, yet fails the build on MEDIUM findings — why, and how do you fix it?

level: seniorimportance: nice to knowfreq 9%

basics

~20 s

trivy-action unsets the severity input for SARIF output unless limit-severities-for-sarif is true, so without a config-file severity Trivy reports and gates on every level. Set that input to true, or split reporting and gating into two steps.

open as a page

After the March 2026 trivy-action tag compromise, what should a team that ran Trivy in CI check, rotate and change?

level: seniorimportance: nice to knowfreq 6%

basics

~20 s

Check whether any job ran a trivy-action tag below 0.35.0, an unpinned setup-trivy or a malicious Trivy 0.69.4, 0.69.5 or 0.69.6 in March 2026; if so, rotate every secret it could reach, then pin all three immutably.

open as a page