Configuration reference
Complete reference for the ringzero { } extension added by the
tech.ringzero.sca plugin. All properties use Gradle’s lazy Property/
Provider types, so they’re compatible with the configuration cache and can be
wired to providers.
Top level
Section titled “Top level”| Property | Type | Default | Description |
|---|---|---|---|
apiKey | Property<String> | — | Scoped API key (rz_key_…) with at least sca:upload + scans:read — see Authentication & permissions. Resolution order: this property → RINGZERO_API_KEY env var → ringzero.apiKey Gradle property. See secure provisioning. |
apiUrl | Property<String> | https://api.ringzero.tech/v1 | API base URL override. Only change this if RingZero support tells you to (e.g. a regional endpoint). Also settable via RINGZERO_API_URL. |
organization | Property<String> | — (required) | Your org id (org_9f3k), from Settings → General. |
project | Property<String> | root project name | Project id (prj_6h2m) or slug. Pin it explicitly to keep scan history stable across renames. If the slug doesn’t exist server-side, the upload fails with 422 unknown_project — RingZero never auto-creates projects from uploads (a leaked upload key must not be able to create org resources). |
suppressionsFile | RegularFileProperty | ringzero-suppressions.yml in the root project | The suppressions file (plugin-side upload coming soon — see the note there). The default is used only when the file exists; explicitly setting a path that doesn’t exist is a configuration error. |
reportsDirectory | DirectoryProperty | build/ringzero | Where the tasks write their machine-readable outputs (snapshot.json, scan.json, summary.json, findings.json). |
scan { }
Section titled “scan { }”Controls what goes into the dependency snapshot.
| Property | Type | Default | Description |
|---|---|---|---|
configurations | ListProperty<String> | ["runtimeClasspath"] | Gradle configurations to resolve. Accepts exact names or globs ("*RuntimeClasspath"), optionally path-qualified per subproject (":benchmark:runtimeClasspath"; unqualified entries apply to every project). A name matching nothing in a given project is skipped silently (not every subproject has every configuration); an entry matching nothing in any project logs a warning. |
excludedConfigurations | ListProperty<String> | [] | Configurations to leave out, same name/glob/path-qualification rules as configurations. The selection is configurations minus excludedConfigurations — exclusion always wins. The motivating pattern is variant-heavy builds: configurations = ["*RuntimeClasspath"] with excludedConfigurations = ["debug*", "*AndroidTest*"]. |
excludedModules | ListProperty<String> | [] | Glob patterns matched against group:name coordinates ("com.acme.internal:*", "*:fastjson"). Matched modules are removed from the snapshot before upload — they never reach RingZero, produce no findings, and don’t count toward snapshot limits. Transitive dependencies reachable only through an excluded module are pruned with it; ones also reachable via a non-excluded path stay. Use this for genuinely out-of-scope artifacts (in-house modules from an internal repository). To silence a known finding on a package you still want inventoried, use a suppression instead. |
includeTransitive | Property<Boolean> | true | Include transitive dependencies. Disabling this is almost never what you want — most vulnerable packages arrive transitively — but it’s available for diagnostics. |
manifests | ListProperty<String> | auto-detected | Manifest paths recorded in the snapshot. Detected per included subproject: build.gradle(.kts), plus gradle.lockfile when dependency locking is enabled. excludeProjects also excludes that project’s manifests. Override only if your build generates manifests in nonstandard locations. |
excludeProjects | ListProperty<String> | [] | Subproject paths (e.g. ":samples", ":docs") to leave out of the aggregated snapshot entirely — their configurations, modules, and manifests. |
Glob syntax (everywhere globs are accepted): * matches any run of
characters within a segment. There is no ** or ?.
failOn { }
Section titled “failOn { }”Policy evaluated by ringzeroCheck after the scan completes. Without a
failOn block, ringzeroCheck still waits for completion and prints the
summary — it just never fails the build.
| Property | Type | Default | Description |
|---|---|---|---|
severity | Property<String> | unset (never fail) | Minimum severity that fails the build: "critical", "high", "medium", or "low". A value of "high" fails on high and critical. |
cvss | Property<Double> | unset | Fail on any finding with a CVSS base score at or above this value (0.0–10.0). |
fixableOnly | Property<Boolean> | false | Only findings with a non-null fixedVersion trip the gate — gate on what the team can action today. Unfixable findings are still reported, they just don’t fail the build. |
reachableOnly | Property<Boolean> | false | Only findings marked reachable trip the gate. Fail-safe rule: findings with no reachability data still gate — unknown ≠ safe. |
gracePeriod | Property<Duration> | unset | Findings whose advisory was published within the period (e.g. PT168H = 7 days, using the finding’s publishedAt) warn instead of fail — a CVE published overnight shouldn’t redden every pipeline before anyone can triage. Pair with expiring suppressions for the long tail. |
unresolvable | Property<Boolean> | false | What ringzeroCheck does when it can’t get an answer: poll timeout (upload.timeout), network loss mid-poll, or scan status: failed. false = warn and pass (fail open), true = fail the build (fail closed) — typical only in release pipelines. This governs ringzeroCheck only: an upload that exhausts its retries always fails ringzeroScan (there is no scan to check), and a deliberate --offline build runs report-only, never an unresolvable event. |
How the thresholds combine: each finding is tested against severity and
cvss independently — either trips the gate. A finding with no CVSS score
(some advisories ship without a vector) is evaluated against severity only;
setting only cvss never consults severity labels. fixableOnly and
reachableOnly are filters on top: a finding must pass a threshold and
every enabled filter to fail the build.
upload { }
Section titled “upload { }”Network behavior for the snapshot upload and status polling.
| Property | Type | Default | Description |
|---|---|---|---|
timeout | Property<Duration> | PT5M | Overall budget for the upload (all attempts and backoff); once ringzeroCheck ships, also its wait-for-completion budget. |
connectTimeout | Property<Duration> | PT10S | TCP/TLS connect timeout per request. |
retries | Property<Int> | 3 | Retry count for the upload request (exponential backoff with jitter; retries I/O errors, 5xx, and 429, honoring Retry-After). Other 4xx responses never retry. |
pollInterval | Property<Duration> | PT5S | Interval between GET /v1/scans/{scanId} polls — reserved for the upcoming ringzeroCheck, unused in v0.1.0. |
Uploads are idempotent: the plugin sends the snapshot’s content hash as an
Idempotency-Key, so a
timeout-then-retry (or two CI jobs racing on the same commit) converges on one
scan instead of creating duplicates.
Network proxy
Section titled “Network proxy”The plugin uses the build JVM’s standard proxy settings — the same ones Gradle
itself uses — set in gradle.properties:
systemProp.https.proxyHost=proxy.internalsystemProp.https.proxyPort=3128systemProp.http.nonProxyHosts=*.internalThere is deliberately no RingZero-specific proxy DSL: ops teams configure the JVM proxy once and every plugin honors it. (The sidecar, which doesn’t run on a JVM, has its own proxy config.)
Suppressions file
Section titled “Suppressions file”Suppress a known, triaged finding without losing sight of it. Suppressions live in a committed YAML file — not the DSL — because they’re governance artifacts: the reason and expiry belong in code review, and the same file format works for direct API integrations.
suppressions: - cve: CVE-2025-4410 # required — CVE or GHSA id package: "com.alibaba:fastjson" # optional glob; omitted = this CVE on any package reason: "Not exploitable: we never deserialize untrusted input" # required expires: 2026-10-01 # optional ISO date; omitted = no expirySemantics:
- Suppressions are uploaded with the snapshot and applied server-side:
matching findings are marked
suppressed, excluded from the scan’s severity counts — and therefore fromfailOngating — but remain visible in the app and the API. reasonis required. The plugin fails configuration on an entry without one.- An expired entry is ignored and produces a build warning naming it —
gates come back automatically, which is the point of
expires. - An entry matching no current finding is an
--info-level note, not a warning: fixed CVEs go quiet gracefully.
Contrast with scan.excludedModules: exclusion removes a module from the
snapshot entirely (no inventory, no findings, invisible); suppression keeps
the dependency and the finding visible but un-gated. Prefer suppression for
anything you may need to explain later.
Tasks: inputs and outputs
Section titled “Tasks: inputs and outputs”| Task | Inputs | Outputs (under reportsDirectory, default build/ringzero/) |
|---|---|---|
ringzeroScan | Resolved dependency graphs of the selected configurations; scan { } settings; organization/project/apiUrl; whether an upload will happen | snapshot.json (the uploaded snapshot); scan.json — { "id": "scn_7g8h", "url": "…" } after an upload, { "uploaded": false, "reason": "no-api-key" | "offline" } in report-only mode |
ringzeroCheck Coming soon | scan.json; the failOn { } policy | Build success/failure; console summary; summary.json — always written, pass or fail: { scanId, url, status, findings: {…}, suppressed, policy: { violated, threshold, violations } }; findings.json (raw findings array) when the key has findings:read |
summary.json exists so CI can annotate PRs without scraping console output.
Build behavior
Section titled “Build behavior”Up-to-date checking
Section titled “Up-to-date checking”ringzeroScan is up-to-date when the resolved dependency graph (after
exclusions), project, organization, apiUrl, and whether an upload
would happen are all unchanged since the last run — the task then skips the
upload and reuses the previous scan id. Consequences worth knowing:
- Advisory data changes server-side. RingZero re-matches stored snapshots
against new advisories continuously — new findings appear in the app — but
the gate sees new advisories only on the next real upload (in practice: the
next dependency change, or
--rerun). - Providing an API key after a report-only run counts as a change: the next build re-executes and uploads instead of reporting up-to-date.
- Force a fresh upload any time with
./gradlew ringzeroScan --rerun.
The tasks are configuration-cache compatible but never build-cacheable: the upload is a side effect, and a cache hit that silently skipped it would be a correctness bug.
Offline builds and report-only mode
Section titled “Offline builds and report-only mode”Two situations put ringzeroScan in the same report-only mode: Gradle’s
--offline flag, and no API key resolving from any
source. In both, the task still resolves the
graph and writes snapshot.json (handy for inspecting what would upload),
skips the upload, logs which mode it is in, and succeeds — a missing key
is never an error. scan.json records the outcome as
{ "uploaded": false, "reason": "no-api-key" | "offline" }.
Deliberate offline is never a failOn.unresolvable failure — that property
governs unexpected unreachability only.
Configuration precedence
Section titled “Configuration precedence”For every setting with more than one source, the plugin resolves in this order (first match wins):
| Priority | Source | Example |
|---|---|---|
| 1 | ringzero { } DSL | apiUrl = "https://api.eu.ringzero.tech/v1" |
| 2 | Environment variable | RINGZERO_API_URL=… |
| 3 | Gradle property | ringzero.apiUrl=… in ~/.gradle/gradle.properties |
This applies to scalar top-level properties (apiKey, apiUrl,
organization, project), which all have RINGZERO_* env and ringzero.*
Gradle-property forms. List-valued and block settings (scan { },
failOn { }, upload { }, suppressionsFile, reportsDirectory) are
DSL-only.