skip to content

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%

answer

  1. one scan, several consumers
  2. a .tpl file behind an @
  3. an interchange standard for analysers
  4. a snapshot, not a findings list

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.

solid answer

~40 s

Each consumer reads a different shape. JUnit XML comes from `--format template --template "@contrib/junit.tpl"`; the `@` means a file, which must end in `.tpl`, and the shipped templates live in `contrib/` (also `html.tpl`, `gitlab.tpl`, `gitlab-codequality.tpl`, `asff.tpl`). A code-scanning dashboard reads SARIF 2.1.0 from `--format sarif`, where CRITICAL and HIGH map to level `error`, MEDIUM to `warning`, LOW and UNKNOWN to `note`. A dependency graph takes `--format github`, a GitHub dependency snapshot of the packages found, not of the vulnerabilities. `--format json` is the complete record (since 0.67 it lists all packages by default) and the input for later conversion. Write each to a file with `--output`; `--template` without `--format template` is ignored with a warning.

go deeper

for a junior

Recall the main formats: table by default, json for tools, sarif for code-scanning dashboards, template with a .tpl file for JUnit or HTML.

for a middle

Explain how --format template and --template work together, the SARIF severity-to-level mapping, and that --format github is a dependency snapshot.

for a senior

Show you design the outputs: one full JSON scan as the record, rendered formats written with --output, and a separate gate because --severity trims every format.

for a principal

Decide which consumers deserve which format across many pipelines, and why one archived JSON report per build makes later re-rendering and audits cheap.

## One scan, several readers A **Trivy** scan has one result and many possible renderings, selected with `--format` (`-f`) and written to the terminal or to a file with `--output` (`-o`). Trivy 0.74 accepts `table` (the default), `json`, `template`, `sarif`, `cyclonedx`, `spdx`, `spdx-json`, `github` and `cosign-vuln`. The CycloneDX and SPDX outputs are bills of materials and belong with SBOM work; for CI reporting the interesting four are below. | Consumer | Format | What it contains | |---|---|---| | A person reading the job log | `table` | summary and per-target tables | | Other tools, archives, a gate script | `json` | the full result: findings plus package metadata | | A test-results or custom report view | `template` + `--template` | whatever the Go template renders | | A code-scanning dashboard | `sarif` | SARIF 2.1.0 results with rules, levels and locations | | A dependency graph | `github` | a GitHub dependency snapshot of the packages found | ## JSON: the complete record JSON is the most faithful output: `SchemaVersion`, artifact metadata and one entry per target under `Results`, with `Vulnerabilities`, `Misconfigurations`, `Secrets` and `Licenses` arrays. Since 0.67.0 `--list-all-pkgs` defaults to true, so the JSON also lists every package, vulnerable or not. Only `VulnerabilityID`, `PkgName`, `InstalledVersion` and `Severity` are guaranteed to be filled, so a script reading it must tolerate empty fields. Because `trivy convert` can re-render a saved JSON report into other formats, many pipelines scan once to JSON and derive the rest. ## Templates: JUnit, HTML and the rest - `--format template` needs `--template`. The value is either an inline Go template or `@` plus a path; a path must end in `.tpl` or Trivy refuses it. - The shipped templates are in `contrib/`: `junit.tpl`, `html.tpl`, `gitlab.tpl`, `gitlab-codequality.tpl` and `asff.tpl`. The official container image copies them to `contrib/`, the archives include them, and the rpm and deb packages install them under `/usr/local/share/trivy/templates`. A binary dropped on a runner's PATH has none of them, so `@contrib/junit.tpl` fails until the file is supplied. - Templates can use sprig functions, so a one-line template can print counts per severity. - `--template` without `--format template` is ignored with a warning and the default table is written, often into a file named `report.xml` that the test-results step then cannot parse. ## SARIF: what the dashboard receives `--format sarif` writes SARIF 2.1.0 with one rule per vulnerability, check or secret rule. Trivy maps its severity to a SARIF level and also writes a numeric `security-severity` property: 1. CRITICAL and HIGH become level `error`. 2. MEDIUM becomes `warning`. 3. LOW and UNKNOWN become `note`. Misconfigurations and secrets carry start and end lines in the scanned file, so a dashboard can point at the line. A vulnerability in an OS package found in an image has no Dockerfile line to point at; its location is a path built from the image's repository name. Uploading the file and what the dashboard does with it are the code-scanning platform's business; Trivy's job ends at a valid file. ## `github`: packages, not findings `--format github` produces a **dependency snapshot**: the OS and language packages Trivy found, in the shape GitHub's dependency submission API accepts, so the repository's dependency graph knows what is inside an image. It carries no vulnerability findings. The official GitHub Action can submit it when given a token with the right permission. ## Practical shape of a pipeline - Scan once to JSON with every severity and archive it. - Render SARIF and JUnit for their consumers, each with `--output` so formats do not mix on stdout. - Gate separately, because `--severity` trims every format, SARIF included. ## Mistakes seen in pipelines - Expecting `--format github` to raise vulnerability alerts; it only describes packages. - Writing two formats to stdout in one job and parsing the interleaved log instead of separate `--output` files. - Building a dashboard from a gated run and wondering why MEDIUM findings never appear. - Parsing the table output with text tools when JSON carries the same data with stable field names.

  • Why does `--template "@contrib/junit.tpl"` work in Trivy's container image but fail on a runner that downloaded only the binary?
    The image copies the shipped `.tpl` files into `contrib/`, and the release archives and rpm or deb packages ship them too, the packages under `/usr/local/share/trivy/templates`. A bare binary has no template files, so the relative path resolves to nothing. Vendor the template into the repository or point `@` at the packaged location.
  • Does `--severity HIGH,CRITICAL` also trim the SARIF Trivy writes?
    Yes. Filtering happens before rendering, so every format, SARIF included, contains only the severities that passed. A dashboard fed from a gated run therefore never sees MEDIUM or LOW results. Teams that want the dashboard complete produce the SARIF from a separate run with all severities and `--exit-code 0`.

saying these in an interview costs you the question

  • --format github uploads vulnerabilities as code-scanning alerts.
  • Passing --template on its own switches Trivy to template output.
  • A template file referenced with @ can have any extension.
  • Trivy's CLI writes every severity into SARIF, whatever --severity says.
  • Every field in Trivy's JSON vulnerability entries is always populated.