trivy
This module provides Dagger functions for scanning Docker images and filesystemdirectories using Trivy, the open-source vulnerability scanner from Aqua Security.
It demonstrates how to configure containers with Trivy, accept optional inputs
like scan severity and credentials, and return results as Dagger file outputs or
raw text.
The `ScanFilesystem` function scans a directory for vulnerabilities and returns
a Trivy report as a file. The `ScanImage` function scans a container image by
reference and returns the vulnerability report as plain text. The `Sbom`
function writes a CycloneDX or SPDX bill of materials for one platform of a
digest-pinned image. These functions serve as a reference for integrating
Trivy into secure CI/CD pipelines using Dagger.
Installation
dagger install github.com/stuttgart-things/dagger/trivy@v0.131.0Entrypoint
Return Type
Trivy Example
dagger -m github.com/stuttgart-things/dagger/trivy@13be045b822d23b5c6335452a4f4dc3c0b2283e7 call \
func (m *MyModule) Example() *dagger.Trivy {
return dag.
Trivy()
}@function
def example() -> dagger.Trivy:
return (
dag.trivy()
)@func()
example(): Trivy {
return dag
.trivy()
}Types
Trivy 🔗
sbom() 🔗
Sbom writes a software bill of materials for one platform of a published image – the document a signed attestation on that image’s digest carries.
The result covers exactly one platform, linux/amd64 unless told otherwise. A multi-arch release is an index, and an SBOM describes an image, not an index: left to itself trivy resolves the index to the platform it happens to run on, so the same call would describe a different image on a different runner. For another platform, call again. A platform the index does not carry is an error.
imageRef has to be pinned to a digest (repo@sha256:…). An inventory of whatever a tag pointed at while this ran is an inventory of nothing in particular, and could not be attached to the digest it claims to describe. Resolve a tag first, e.g. with the crane module’s Digest.
Unlike ScanImage, trivy’s exit code is not discarded. An SBOM run that cannot pull the image must not hand back an empty document: attested and signed, an empty inventory is a signed claim that the image contains nothing.
The image is always read from the registry (–image-src remote), never from a local daemon, so the document describes what was published.
Return Type
File !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| imageRef | String ! | - | Image reference pinned to a digest (e.g., “ghcr.io/org/app@sha256:…”) |
| registryUser | Secret | - | No description provided |
| registryPassword | Secret | - | No description provided |
| platform | String | "linux/amd64" | The one platform the document covers |
| format | String | "cyclonedx" | cyclonedx or spdx-json |
| trivyVersion | String | "0.64.1" | No description provided |
Example
dagger -m github.com/stuttgart-things/dagger/trivy@13be045b822d23b5c6335452a4f4dc3c0b2283e7 call \
sbom --image-ref stringfunc (m *MyModule) Example(imageRef string) *dagger.File {
return dag.
Trivy().
Sbom(imageRef)
}@function
def example(imageref: str) -> dagger.File:
return (
dag.trivy()
.sbom(imageref)
)@func()
example(imageRef: string): File {
return dag
.trivy()
.sbom(imageRef)
}scanFilesystem() 🔗
ScanFilesystem scans a directory and returns Trivy’s JSON report, enriched with a ParsedSummary listing the findings at the requested severities.
A filesystem scan turns up two unrelated kinds of finding: package vulnerabilities read out of lockfiles, and secrets matched in the files themselves. Until now only the secrets were parsed, so a directory whose go.sum pinned a vulnerable dependency produced ParsedSummary: null and a successful exit – the report carried the CVE while the summary said nothing. Both kinds are collected now. Secrets keep their existing “[SECRET] …” prefix, so the two remain distinguishable in one list and ParsedSummary stays the array it always was.
Trivy’s own exit code is deliberately discarded (|| true): it exits 0 on
findings anyway unless asked otherwise, and swallowing it keeps a scan that
found something from being confused with a scan that could not run. The
verdict is taken from the parsed report instead, which is what makes the
summary and the gate agree by construction.
A scan that cannot run at all is still an error – unparseable output leaves nothing to reason about, and reporting that as “nothing found” would be the worst answer available.
With failOnFinding set this returns an error, so a dagger call
scan-filesystem ... export --path report.json never reaches the export. To
both keep the report and gate the build, call twice – once to export, once
to gate. The second call hits Dagger’s cache and costs nothing. ScanImage
and the go module’s Govulncheck have the same shape for the same reason.
Return Type
File !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| src | Directory ! | - | No description provided |
| severity | String | "HIGH,CRITICAL" | No description provided |
| trivyVersion | String | "0.64.1" | No description provided |
| failOnFinding | Boolean | false | Fail when the scan finds anything at the requested severities. Off by default so that adopting a newer module version cannot turn a green pipeline red on its own; callers that want a gate ask for one. |
Example
dagger -m github.com/stuttgart-things/dagger/trivy@13be045b822d23b5c6335452a4f4dc3c0b2283e7 call \
scan-filesystem --src DIR_PATHfunc (m *MyModule) Example(src *dagger.Directory) *dagger.File {
return dag.
Trivy().
Scanfilesystem(src)
}@function
def example(src: dagger.Directory) -> dagger.File:
return (
dag.trivy()
.scanfilesystem(src)
)@func()
example(src: Directory): File {
return dag
.trivy()
.scanFilesystem(src)
}scanImage() 🔗
ScanImage scans a published container image and returns Trivy’s JSON report, enriched with a ParsedSummary listing the findings at the requested severities.
Trivy’s own exit code is deliberately discarded (|| true): it exits 0 on
findings anyway unless asked otherwise, and swallowing it keeps a scan that
found something from being confused with a scan that could not run. The
verdict is taken from the parsed report instead, which is also what makes
the summary and the gate agree by construction.
A scan that cannot run at all is still an error – an unreachable registry or an image that does not exist leaves no parseable JSON behind, and reporting that as “nothing found” would be the worst answer available.
With failOnFinding set this returns an error, so a dagger call scan-image
... export --path report.json never reaches the export. To both keep the
report and gate the build, call twice – once to export, once to gate. The
second call hits Dagger’s cache and costs nothing. Govulncheck in the go
module has the same shape for the same reason.
Return Type
File !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| imageRef | String ! | - | Fully qualified image reference (e.g., “ttl.sh/my-repo:1.0.0”) |
| registryUser | Secret | - | No description provided |
| registryPassword | Secret | - | No description provided |
| severity | String | "HIGH,CRITICAL" | No description provided |
| trivyVersion | String | "0.64.1" | No description provided |
| failOnFinding | Boolean | false | Fail when the scan finds anything at the requested severities. Off by default so that adopting a newer module version cannot turn a green pipeline red on its own; callers that want a gate ask for one. |
Example
dagger -m github.com/stuttgart-things/dagger/trivy@13be045b822d23b5c6335452a4f4dc3c0b2283e7 call \
scan-image --image-ref stringfunc (m *MyModule) Example(imageRef string) *dagger.File {
return dag.
Trivy().
Scanimage(imageRef)
}@function
def example(imageref: str) -> dagger.File:
return (
dag.trivy()
.scanimage(imageref)
)@func()
example(imageRef: string): File {
return dag
.trivy()
.scanImage(imageRef)
}