Dagger
Search

security

No long description provided.

Installation

dagger install github.com/context-and-oss/cicd/src/security@v5.1.5

Entrypoint

Return Type
Security
Example
dagger -m github.com/context-and-oss/cicd/src/security@e510ca0bff49e501bdb77cbdb6b12eb0bf10c447 call \
func (m *MyModule) Example() *dagger.Security  {
	return dag.
			Security()
}
@function
def example() -> dagger.Security:
	return (
		dag.security()
	)
@func()
example(): Security {
	return dag
		.security()
}

Types

Security 🔗

Security is the Dagger module for the scanners: SAST and secrets over the source tree (ScanSource), vulnerabilities in a built image (ScanImage) or a built archive (ScanArchive), licence policy over the dependency graph (ScanLicenses), a frozen SBOM against today’s database (ScanSbom), and DAST against a running application (ScanWeb). Every entry returns an evidence envelope in which a scanner that did not run is an errors[] entry, never a clean result.

scanArchive() 🔗

ScanArchive scans the “archive” artifact shape (a built directory/zip rather than an OCI image — see ADR-001):

  • dependency vulnerabilities + CycloneDX SBOM come from the SOURCE (Trivy filesystem scan over lockfiles — bundled/published artifacts yield garbage dependency data);
  • secrets detection (Gitleaks) targets the built OUTPUT — the bundled-.env class of leak only exists in the artifact.

Returns an aggregated evidence envelope shaped like ScanImage’s (FindingsBySeverity, CycloneDXReport, SARIFReport).

Return Type
File !
Arguments
NameTypeDefault ValueDescription
srcDirectory !-

Source directory the archive was built from (lockfile SBOM + dependency vuln scan).

outputDirDirectory !-

Built archive output directory to secrets-scan (e.g. a vite dist/, dotnet publish output). Named outputDir, not output: the dagger CLI reserves -o/–output on dagger call, so a param literally named “output” can never be passed.

failOnString !"HIGH"

Minimum severity that fails the pipeline: CRITICAL|HIGH|MEDIUM|LOW|NONE

commitShaString !-

Git commit SHA for evidence provenance.

branchString !-

Git branch name for evidence provenance.

repoUrlString !-

Repository URL for evidence provenance.

nugetFeedUrlString -

Private NuGet feed index URL, used only by the lockfile-free dependency graph fallback — see ScanSource.

nugetTokenSecret -

Token for nugetFeedUrl. Handed to NuGet as a secret and never written to the evidence envelope.

artifactOnlyBoolean -

Only scan built-output secrets. The orchestrator separately declares whether dependency assessment ran or was intentionally deferred.

vulnAcceptString -

Vulnerability findings to accept, ONE PER LINE, each as “=” — e.g. “CVE-2026-81871=the exporter only reaches the Dagger engine’s own telemetry endpoint on the runner”. An accepted identifier is removed from the severity counts, from findings[] and from the gate, and is disclosed in warnings[] with the reason given; the row stays in the CycloneDX report, so the vulnerability is still in the evidence. A reason is mandatory, patterns are refused, and an acceptance matching nothing warns so a stale one is visible rather than a standing permission. The separator is a newline and never a comma, because a reason is prose that contains commas — see splitVulnAcceptances in vulnallow.go for what the CLI does to a []string here.

Example
dagger -m github.com/context-and-oss/cicd/src/security@e510ca0bff49e501bdb77cbdb6b12eb0bf10c447 call \
 scan-archive --src DIR_PATH --output-dir DIR_PATH --fail-on string --commit-sha string --branch string --repo-url string
func (m *MyModule) Example(src *dagger.Directory, outputDir *dagger.Directory, failOn string, commitSha string, branch string, repoUrl string) *dagger.File  {
	return dag.
			Security().
			Scanarchive(src, outputDir, failOn, commitSha, branch, repoUrl)
}
@function
def example(src: dagger.Directory, outputdir: dagger.Directory, failon: str, commitsha: str, branch: str, repourl: str) -> dagger.File:
	return (
		dag.security()
		.scanarchive(src, outputdir, failon, commitsha, branch, repourl)
	)
@func()
example(src: Directory, outputDir: Directory, failOn: string, commitSha: string, branch: string, repoUrl: string): File {
	return dag
		.security()
		.scanArchive(src, outputDir, failOn, commitSha, branch, repoUrl)
}

scanImage() 🔗

ScanImage runs secrets entropy scan (TruffleHog) and image vulnerability scan (Trivy) on a built container image. Returns an aggregated evidence envelope.

Return Type
File !
Arguments
NameTypeDefault ValueDescription
imageContainer !-

Built container image to scan.

failOnString !"HIGH"

Minimum severity that fails the pipeline: CRITICAL|HIGH|MEDIUM|LOW|NONE

verifySecretsBoolean !false

Restrict secrets findings to live-verified credentials (adds TruffleHog’s –only-verified). Opt-in “heavier secrets” gate; probes secret providers.

commitShaString !-

Git commit SHA for evidence provenance.

branchString !-

Git branch name for evidence provenance.

repoUrlString !-

Repository URL for evidence provenance.

vulnAcceptString -

Vulnerability findings to accept, ONE PER LINE, each as “=” — e.g. “CVE-2026-81871=the exporter only reaches the Dagger engine’s own telemetry endpoint on the runner”. An accepted identifier is removed from the severity counts, from findings[] and from the gate, and is disclosed in warnings[] with the reason given; the row stays in the CycloneDX report, so the vulnerability is still in the evidence. A reason is mandatory, patterns are refused, and an acceptance matching nothing warns so a stale one is visible rather than a standing permission. The separator is a newline and never a comma, because a reason is prose that contains commas — see splitVulnAcceptances in vulnallow.go for what the CLI does to a []string here.

Example
dagger -m github.com/context-and-oss/cicd/src/security@e510ca0bff49e501bdb77cbdb6b12eb0bf10c447 call \
 scan-image --image IMAGE:TAG --fail-on string --verify-secrets boolean --commit-sha string --branch string --repo-url string
func (m *MyModule) Example(image *dagger.Container, failOn string, verifySecrets bool, commitSha string, branch string, repoUrl string) *dagger.File  {
	return dag.
			Security().
			Scanimage(image, failOn, verifySecrets, commitSha, branch, repoUrl)
}
@function
def example(image: dagger.Container, failon: str, verifysecrets: bool, commitsha: str, branch: str, repourl: str) -> dagger.File:
	return (
		dag.security()
		.scanimage(image, failon, verifysecrets, commitsha, branch, repourl)
	)
@func()
example(image: Container, failOn: string, verifySecrets: boolean, commitSha: string, branch: string, repoUrl: string): File {
	return dag
		.security()
		.scanImage(image, failOn, verifySecrets, commitSha, branch, repoUrl)
}

scanLicenses() 🔗

ScanLicenses runs a Trivy license scan over the source tree and judges every license it finds against an allowlist of Trivy license categories. Returns an evidence envelope.

A separate step from ScanSource rather than a fourth leg inside it, and the separation is semantic before it is structural. A license finding is a compliance judgement, not a vulnerability: it has no CVE, no fix version and no vendor severity, and folding it into ScanSource’s merged counts would let a copyleft dependency consume the budget of a --fail-on HIGH gate that exists to stop exploitable code shipping. The two verdicts answer different questions and belong on different envelopes. licenseflags.go carries the parallel argument for keeping the Trivy invocations apart.

Warn mode lowers the EFFECTIVE threshold to NONE rather than post-processing the verdict, which is what keeps the envelope internally consistent: the row verdict, FailOnSeverity and Passed all describe one threshold, and no reader has to reconcile a HIGH finding with a passing gate. It reuses the NONE semantics gatedPassed already defines, which means warn mode inherits the property that matters most — a scan that could not RUN still fails. Per that function’s reasoning, “do not fail me over findings” never meant “do not tell me the scanner was skipped”, and an advisory license policy is no more entitled to hide a broken scanner than --fail-on NONE is.

Return Type
File !
Arguments
NameTypeDefault ValueDescription
srcDirectory !-

Source directory to scan.

pathString -

Subdirectory within src to scan. Empty string scans the entire src.

exclude[String ! ] -

Directory names to exclude (e.g. “vendor”, “node_modules”). Relative to path if set, otherwise relative to src root.

licenseExclude[String ! ] -

Glob patterns whose license declarations are reported but not judged against the policy, e.g. “docs/” or “/node_modules/**”. Matched against each declaration’s file path, relative to the scanned root. Every exclusion is counted and every pattern is recorded in the evidence — see licenseexclude.go.

licenseGateString !"warn"

Whether license findings fail the gate: “gate” or “warn”. There is deliberately no “off” — see licensereport.go.

licenseAllow[String ! ] -

Trivy license categories that are permitted. Empty uses cicd’s default policy (permissive, notice, unencumbered); anything outside the allowlist is a finding, including a category nothing recognises.

licenseFullBoolean !true

Read licenses from source headers and LICENSE files as well as from package metadata. On by default because metadata-only mode never reads the tree’s own LICENSE, which in an ecosystem whose manifests carry no license field — Go among them — can be all there is to read. It does not read dependency licences: those come from installed package metadata, and a graph with none read is SKIPPED (licencecoverage.go).

failOnString !"HIGH"

Minimum severity that fails the pipeline: CRITICAL|HIGH|MEDIUM|LOW|NONE. Ignored when licenseGate is “warn”.

commitShaString !-

Git commit SHA for evidence provenance.

branchString !-

Git branch name for evidence provenance.

repoUrlString !-

Repository URL for evidence provenance.

sbomFile -

Optional generated application inventory. Scanned instead of filesystem dependencies, using the same Trivy scanner and licence policy.

sourceOnlyBoolean -

Only source-file declarations; dependency licence assessment is outside this scan. Always enables source-file collection. Incompatible with sbom.

nugetFeedUrlString -

Private NuGet feed index URL. A NuGet package’s licence is read from its .nuspec, which only a restore puts on disk, so every .NET tree is restored for this scan — lockfile or not — and a restore that cannot reach the consumer’s feed reads no licence for what it could not fetch.

nugetTokenSecret -

Token for nugetFeedUrl. Handed to NuGet as a secret and never written to the evidence envelope.

Example
dagger -m github.com/context-and-oss/cicd/src/security@e510ca0bff49e501bdb77cbdb6b12eb0bf10c447 call \
 scan-licenses --src DIR_PATH --license-gate string --license-full boolean --fail-on string --commit-sha string --branch string --repo-url string
func (m *MyModule) Example(src *dagger.Directory, licenseGate string, licenseFull bool, failOn string, commitSha string, branch string, repoUrl string) *dagger.File  {
	return dag.
			Security().
			Scanlicenses(src, licenseGate, licenseFull, failOn, commitSha, branch, repoUrl)
}
@function
def example(src: dagger.Directory, licensegate: str, licensefull: bool, failon: str, commitsha: str, branch: str, repourl: str) -> dagger.File:
	return (
		dag.security()
		.scanlicenses(src, licensegate, licensefull, failon, commitsha, branch, repourl)
	)
@func()
example(src: Directory, licenseGate: string, licenseFull: boolean, failOn: string, commitSha: string, branch: string, repoUrl: string): File {
	return dag
		.security()
		.scanLicenses(src, licenseGate, licenseFull, failOn, commitSha, branch, repoUrl)
}

scanSbom() 🔗

ScanSbom rescans an existing CycloneDX SBOM against today’s vulnerability database. A vulnerability verdict is a frozen SBOM crossed with moving CVE knowledge, so the verdict a release shipped with goes stale the day a new advisory lands — this is the scheduled-rescan entry point ADR-006 names: the consumer feeds back the sbom.cdx.json a commit run froze and gets a fresh evidence envelope. Deliberately wired into no composite; the pipelines scan the tree and the image directly.

Return Type
File !
Arguments
NameTypeDefault ValueDescription
sbomFile !-

CycloneDX SBOM document to rescan. cicd’s own envelopes carry one under the key cyclonedxReport; the commit audit directory ships it as sbom.cdx.json.

failOnString !"HIGH"

Minimum severity that fails the pipeline: CRITICAL|HIGH|MEDIUM|LOW|NONE

commitShaString !-

Git commit SHA for evidence provenance — the commit the SBOM was frozen at, so the rescan’s envelope names the release it grades.

branchString !-

Git branch name for evidence provenance.

repoUrlString !-

Repository URL for evidence provenance.

applicationInventoryBoolean -

Refuse a non-empty application inventory that Trivy resolved to no packages.

vulnAcceptString -

Vulnerability findings to accept, ONE PER LINE, each as “=” — e.g. “CVE-2026-81871=the exporter only reaches the Dagger engine’s own telemetry endpoint on the runner”. An accepted identifier is removed from the severity counts, from findings[] and from the gate, and is disclosed in warnings[] with the reason given; the row stays in the CycloneDX report, so the vulnerability is still in the evidence. A reason is mandatory, patterns are refused, and an acceptance matching nothing warns so a stale one is visible rather than a standing permission. The separator is a newline and never a comma, because a reason is prose that contains commas — see splitVulnAcceptances in vulnallow.go for what the CLI does to a []string here.

Example
dagger -m github.com/context-and-oss/cicd/src/security@e510ca0bff49e501bdb77cbdb6b12eb0bf10c447 call \
 scan-sbom --sbom file:path --fail-on string --commit-sha string --branch string --repo-url string
func (m *MyModule) Example(sbom *dagger.File, failOn string, commitSha string, branch string, repoUrl string) *dagger.File  {
	return dag.
			Security().
			Scansbom(sbom, failOn, commitSha, branch, repoUrl)
}
@function
def example(sbom: dagger.File, failon: str, commitsha: str, branch: str, repourl: str) -> dagger.File:
	return (
		dag.security()
		.scansbom(sbom, failon, commitsha, branch, repourl)
	)
@func()
example(sbom: File, failOn: string, commitSha: string, branch: string, repoUrl: string): File {
	return dag
		.security()
		.scanSbom(sbom, failOn, commitSha, branch, repoUrl)
}

scanSource() 🔗

ScanSource runs SAST (Semgrep), secrets detection (Gitleaks), and filesystem vulnerability scan (Trivy) on source code. Returns an aggregated evidence envelope.

Return Type
File !
Arguments
NameTypeDefault ValueDescription
srcDirectory !-

Source directory to scan.

pathString -

Subdirectory within src to scan. Empty string scans the entire src.

exclude[String ! ] -

Directory names to exclude (e.g. “vendor”, “node_modules”). Relative to path if set, otherwise relative to src root.

sastRulesetsString !"p/security-audit"

Comma-separated semgrep rulesets: registry packs (e.g. “p/security-audit,p/owasp-top-ten”) or a config path in the scanned tree (e.g. “./semgrep.yml”). “auto” is rejected — it cannot run with telemetry off, which cicd always sets. Never pass empty. p/security-audit is read from a pinned semgrep-rules commit and must load every rule it declares; any other registry pack is fetched live and reported as unpinned.

failOnString !"HIGH"

Minimum severity that fails the pipeline: CRITICAL|HIGH|MEDIUM|LOW|NONE

commitShaString !-

Git commit SHA for evidence provenance.

branchString !-

Git branch name for evidence provenance.

repoUrlString !-

Repository URL for evidence provenance.

nugetFeedUrlString -

Private NuGet feed index URL, used only by the lockfile-free dependency graph fallback: a .NET tree that commits no packages.lock.json is restored in a pinned SDK so there is a graph to scan, and a restore that cannot reach the consumer’s feed cannot produce one.

nugetTokenSecret -

Token for nugetFeedUrl. Handed to NuGet as a secret and never written to the evidence envelope.

codeOnlyBoolean -

Scan source code only. Dependency checks must be supplied separately from app-specific inventories by the orchestrator; no filesystem SCA runs.

vulnAcceptString -

Vulnerability findings to accept, ONE PER LINE, each as “=” — e.g. “CVE-2026-81871=the exporter only reaches the Dagger engine’s own telemetry endpoint on the runner”. An accepted identifier is removed from the severity counts, from findings[] and from the gate, and is disclosed in warnings[] with the reason given; the row stays in the CycloneDX report, so the vulnerability is still in the evidence. A reason is mandatory, patterns are refused, and an acceptance matching nothing warns so a stale one is visible rather than a standing permission. The separator is a newline and never a comma, because a reason is prose that contains commas — see splitVulnAcceptances in vulnallow.go for what the CLI does to a []string here.

Example
dagger -m github.com/context-and-oss/cicd/src/security@e510ca0bff49e501bdb77cbdb6b12eb0bf10c447 call \
 scan-source --src DIR_PATH --sast-rulesets string --fail-on string --commit-sha string --branch string --repo-url string
func (m *MyModule) Example(src *dagger.Directory, sastRulesets string, failOn string, commitSha string, branch string, repoUrl string) *dagger.File  {
	return dag.
			Security().
			Scansource(src, sastRulesets, failOn, commitSha, branch, repoUrl)
}
@function
def example(src: dagger.Directory, sastrulesets: str, failon: str, commitsha: str, branch: str, repourl: str) -> dagger.File:
	return (
		dag.security()
		.scansource(src, sastrulesets, failon, commitsha, branch, repourl)
	)
@func()
example(src: Directory, sastRulesets: string, failOn: string, commitSha: string, branch: string, repoUrl: string): File {
	return dag
		.security()
		.scanSource(src, sastRulesets, failOn, commitSha, branch, repoUrl)
}

scanWeb() 🔗

ScanWeb runs OWASP ZAP dynamic analysis against a running application.

The application reaches the scanner one of three ways, and exactly one of them may be supplied:

--target     a URL the Dagger engine can route to. The consumer started the
             app; cicd cannot know what is behind the address.
--service    tcp://host:port — an app running on the CI runner, tunnelled in
             by the Dagger CLI. Same provenance position as --target, and
             the only route that reaches a composed stack (docker compose
             up) on the runner's loopback, which no URL from inside the
             engine can.
--container  an image cicd starts itself, normally build.Build's output.
             The only source whose evidence can say the running code was
             built from this commit.

Three scan modes: baseline (passive), api (driven from an OpenAPI/SOAP/ GraphQL document) and full (active — sends attack payloads; only against an environment you own).

The evidence states what the scan could not test. A scan against an application in a non-production profile is an application-layer regression detector, not a security attestation of the deployed system, and the warnings[] lines say so rather than leaving a green envelope to imply otherwise.

Return Type
File !
Arguments
NameTypeDefault ValueDescription
targetString -

Full HTTP/S URL of a live target the engine can reach. One of –target, –service or –container.

serviceService -

A service to bind and scan, e.g. –service=tcp://localhost:5000 for an application running on the CI runner. One of –target, –service or –container.

containerContainer -

A container cicd starts as a service and scans. One of –target, –service or –container.

portInteger !8080

Port the application answers on. Ignored for –target, which carries its own.

schemeString !"http"

http or https. Ignored for –target.

basePathString !"/"

Path the application is served under. Ignored for –target.

healthPathString -

Path the readiness probe requests. Defaults to the base path.

readyTimeoutSecondsInteger !120

How long to wait for the target to answer before recording a non-run.

modeString !"baseline"

Scan mode: baseline (passive) | api (from a document) | full (active).

apiSpecFile -

API document to drive mode=api from. Mutually exclusive with –api-spec-path.

apiSpecPathString -

Path the target serves its API document on, e.g. /swagger/v1/swagger.json. Mutually exclusive with –api-spec.

apiFormatString !"openapi"

Format of the API document: openapi | soap | graphql.

ruleConfigFile -

ZAP rule configuration (-c). Every rule it names is reported in the evidence, including any that raised no alert.

maxDurationMinutesInteger !0

Cap the whole scan at this many minutes (ZAP -T). 0 means no cap; an uncapped active scan against a slow target can run for hours.

profileString -

The configuration the application ran under, e.g. “Staging”. Recorded in the evidence and never interpreted — cicd cannot check the claim.

authenticatedBoolean -

Set when the scan exercised a real login. Absent means the findings describe the anonymous surface only, and the evidence says so.

failOnString !"HIGH"

Minimum severity that fails the pipeline: CRITICAL|HIGH|MEDIUM|LOW|NONE. ZAP’s maximum risk is High, so CRITICAL can never fail over findings and the evidence warns when it is used.

commitShaString !-

Git commit SHA for evidence provenance.

branchString !-

Git branch name for evidence provenance.

repoUrlString !-

Repository URL for evidence provenance.

Example
dagger -m github.com/context-and-oss/cicd/src/security@e510ca0bff49e501bdb77cbdb6b12eb0bf10c447 call \
 scan-web --port integer --scheme string --base-path string --ready-timeout-seconds integer --mode string --api-format string --max-duration-minutes integer --fail-on string --commit-sha string --branch string --repo-url string
func (m *MyModule) Example(port int, scheme string, basePath string, readyTimeoutSeconds int, mode string, apiFormat string, maxDurationMinutes int, failOn string, commitSha string, branch string, repoUrl string) *dagger.File  {
	return dag.
			Security().
			Scanweb(port, scheme, basePath, readyTimeoutSeconds, mode, apiFormat, maxDurationMinutes, failOn, commitSha, branch, repoUrl)
}
@function
def example(port: int, scheme: str, basepath: str, readytimeoutseconds: int, mode: str, apiformat: str, maxdurationminutes: int, failon: str, commitsha: str, branch: str, repourl: str) -> dagger.File:
	return (
		dag.security()
		.scanweb(port, scheme, basepath, readytimeoutseconds, mode, apiformat, maxdurationminutes, failon, commitsha, branch, repourl)
	)
@func()
example(port: number, scheme: string, basePath: string, readyTimeoutSeconds: number, mode: string, apiFormat: string, maxDurationMinutes: number, failOn: string, commitSha: string, branch: string, repoUrl: string): File {
	return dag
		.security()
		.scanWeb(port, scheme, basePath, readyTimeoutSeconds, mode, apiFormat, maxDurationMinutes, failOn, commitSha, branch, repoUrl)
}