DevOps: backend (Maven) dependency advisory scanning — close the Java SCA blind spot #820
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Split out of #818 (which scopes frontend advisory early-warning: Renovate OSV alerts + a nightly
npm auditjob). That issue explicitly leaves the backend out of scope — and right now the entire Java dependency tree has no advisory scanning at all:.gitea/workflows/ci.yml:32-33) runsnpm auditonly —frontend/exclusively.pom.xml, butosvVulnerabilityAlertssurfaces advisories Renovate already tracks — it is not a substitute for a real SCA scan of the resolved Maven tree.So the larger transitive surface — Spring Boot 4, Hibernate, Jetty, Flyway, Jackson, and every transitive Java dependency — could carry a published CVE and nothing in CI would ever tell us. For an application holding private family documents, the backend is the higher-value attack surface, and it's the one currently unwatched.
Goal
A published advisory against any backend (Maven) dependency — direct or transitive — is surfaced on a schedule, on a branch we own, with the same open-or-update tracking-issue mechanism the frontend nightly audit uses.
Scope
osv-scanner(Google) — scans the resolved tree against OSV.dev. Single static binary, fast, no DB download, same OSV source as Renovate'sosvVulnerabilityAlerts→ consistent signal across frontend + backend. Recommended.dependency-check(org.owasp:dependency-check-maven) — richer NVD-based reporting, but downloads/maintains a large NVD cache (slow first run, needs an NVD API key to avoid rate limits) and is noisier..gitea/workflows/nightly.ymlwith a backend-SCA job, sibling to the frontend nightly audit from #818. Audit-only, no build beyond what the scanner needs to resolve the tree (osv-scannerreadspom.xml/ a generated lockfile; dependency-check needs the dependencies resolved).Nightly backend SCA: advisory), labelledsecurity,devops,P1-high. Same dedupe + heartbeat + self-test pattern established in #818 — extract it into a composite action if the two jobs share enough logic (ADR-029 precedent).HIGHand above, matching the frontend gate's--audit-level=high.docs/infrastructure/ci-gitea.mddocumenting the scanner choice, the threshold, false-positive suppression (osv-scanner--configignore file / dependency-check suppression XML), and the runbook for a backend-SCA tracking issue.Acceptance criteria
HIGH+ finding, opens/updates one deduped tracking issue (security,devops,P1-high).docs/infrastructure/ci-gitea.mddocuments scanner choice, threshold, suppression, and runbook.workflow_dispatch(a temporarily lowered threshold or a known-vulnerable test dependency is acceptable proof).Out of scope
Open Decision
osv-scannervs OWASPdependency-check. Recommendation:osv-scanner— same OSV.dev source as the frontend Renovate alerts (one consistent advisory feed), no heavy NVD cache, trivial to pin as a static binary. Choosedependency-checkonly if NVD-specific metadata / CVSS detail is needed over OSV coverage. Resolve before implementation.Notes
🛠️ Tobias Wendt — DevOps & Platform Engineer
Observations
nightly.ymlruns on the self-hosted DooD runner, andosv-scanneris a single static binary — no DB download, no NVD cache, no privileged container. That's exactly the "every added tool is a new failure mode" calculus working inosv-scanner's favour over OWASPdependency-check(which drags in a multi-GB NVD cache + API key + slow cold first run). I back the recommendation.nightly.ymltoday is a singledeploy-stagingjob — #818's nightly audit job and its open-or-update issue logic are still unbuilt (Renovate has literally never run; #818 Phase 0). So "reuse the #818 mechanism" is currently "build the mechanism." Sequencing matters — see Decision Queue.${{ secrets.GITEA_TOKEN }}." Our owndocs/infrastructure/ci-gitea.md §Token Name Differencesays Gitea wants an explicit access-token secret, not the GitHub auto-token. Gitea Actions does inject aGITEA_TOKEN, but its scope on our self-hosted instance is not guaranteed to includeissues:writeon a scheduled (non-PR) run. Confirm it can open an issue before building on it; otherwise a dedicated bot PAT scoped toissuesonly.Recommendations
osv-scannerlike everything else. We pinsemgrep==1.163.0, base images by digest, actions@v4. Install a fixedosv-scannerversion and verify its checksum — nevercurl | shoflatest. A moving scanner version is a non-reproducible nightly that changes its mind about what's vulnerable.deploy-staging— same rule #818 landed on. A backend-SCA finding must produce an independent red/green signal that a green deploy can't mask, and vice versa.backend/), soosv-scannerpointed atpom.xmldoes not automatically see the full transitive Spring Boot 4 / Hibernate / Jetty tree, which is the entire point of this issue. Generate a CycloneDX SBOM in the job (cyclonedx-maven-plugin,mvn cyclonedx:makeAggregateBom) and scan that — it captures the fully-resolved transitive set, offline and reproducible. See Felix's comment for the mechanics.Open Decisions
🛡️ Nora "NullX" Steiner — Application Security Engineer
Observations
ci.yml:32-33) isnpm audit, frontend-only. A deserialization gadget or an SSRF in a transitive Jackson/Hibernate/Jetty dep would ship completely unseen. Glad to see my #818 review note turned into a tracked issue.osv-scannerhits the same OSV.dev feed Renovate'sosvVulnerabilityAlertsuses, so frontend and backend advisories speak one vocabulary. Two scanners against two different advisory databases (OSV vs NVD) means two triage mental models and inevitable "why did X fire here but not there." OSV everywhere.Recommendations
pom.xml. A scan that only reads declaredpom.xmlcoordinates is security theatre for this issue's stated goal. Scan a generated CycloneDX SBOM (full resolved tree) — and make the end-to-end proof use a transitive known-vulnerable dep, not a direct one, so the AC actually exercises what we care about.npm audit --audit-level=highhas a native floor;osv-scannerhistorically exits non-zero on any advisory regardless of CVSS. Don't assume "fail on HIGH+" parity exists out of the box — either use the installed version's severity filter (verify it exists) or post-process the JSON onCVSS ≥ 7.0. Otherwise the job goes red on a LOW transitive advisory and gets muted, which is worse than no scanner.osv-scanner.toml[[IgnoredVulns]]) must require areasonand anexpirydate per entry. A permanent silent ignore is how a real CVE hides behind a stale suppression for "that one unfixable transitive thing." Expiry forces a re-review; document this in the runbook.issues:writeand nothing else. Do not hand the nightly a broadcontents+pull_requestPAT just because Renovate's bot has one. A leaked nightly token should not be able to push code.eval/bash -c. An advisory summary is attacker-influenceable upstream content.Open Decisions (none)
🏛️ Markus Keller — Application Architect
Observations
deploy-obs,reload-caddy,smoke-testare shared bynightly.yml+release.yml). The pattern fits — but note this is a two-caller abstraction, and one of the two callers doesn't exist yet.backend/pom.xml. So "scan the tree" is a design decision, not a given: you are choosing the source-of-truth for the resolved dependency graph. That choice belongs in the doc, ADR-style.Recommendations
.gitea/actions/sca-tracking-issue, or (b) #820 builds the action with #818 as a named adopter-to-be. Inlining first and extracting on the second real caller is the lower-risk path. (Decision Queue.)cyclonedx-maven-plugin) and scanning that decouples "resolve the dependency graph" (Maven's job) from "match against advisories" (the scanner's job). It also means swappingosv-scannerfor anything else later is a one-line change at the scan step, not a rework. That's the boring, low-coupling choice.docs/adr/NNNis optional — but theci-gitea.mdsection must record why OSV-over-NVD and why SBOM-over-raw-pom, so nobody "simplifies" it back to a barepom.xmlscan in six months and silently drops transitive coverage.Open Decisions
👨💻 Felix Brandt — Senior Fullstack Developer
Observations
osv-scanneragainst a barepom.xmldoes not reliably resolve the transitive tree. Maven has no lockfile (I checked — nothing inbackend/), so unlikenpm auditreadingpackage-lock.json, there's no resolved manifest to hand the scanner.osv-scanner's native Maven transitive resolution exists but has been gated behind experimental flags and fetches from Maven Central at scan time — non-deterministic and version-sensitive. I don't want a nightly whose results depend on an experimental resolver's mood.osv-scannerhas an--audit-level=highequivalent. It does not, historically — it exits non-zero on any advisory. That's a behaviour difference from the frontend gate the issue treats as symmetric.Recommendations
cyclonedx-maven-pluginand generate an aggregate BOM, then scan it:./mvnw.)jq. Don't rely on a scanner exit code that fires on LOW. Parseosv.json, keep findings with max CVSS ≥ 7.0, and let that count drive open-or-update vs clean. This also gives you the exact list to put in the issue body.semgrep==1.163.0, digest-pinned images). Download a fixedosv-scannerrelease andsha256sum -cit; fail the job if the checksum doesn't match. Nolatest.GETopen issues labelledsecurity, match the marker substring (e.g.Nightly backend SCA: advisory),PATCHthe body if found elsePOSTa new one. Capture both run URLs as the dedupe proof.grepself-tests already inci.yml): feed the matcher a sample existing-issue title (must match) and an unrelated title (must not),exit 1on either failing. Cheap regression guard that the dedupe doesn't silently start duplicating.npm ci/node_moduleshere — this job is pure Maven + a static binary.checkout→ SDKMAN/Java →mvnw cyclonedx→ scan. Don't copy frontend setup steps in.Open Decisions (none — all of the above are mechanical calls)
🧪 Sara Holt — QA Engineer & Test Strategist
Observations
Recommendations
workflow_dispatchverification dependency should be one pulled in transitively (or temporarily lower the threshold against a real transitive advisory already in the tree). AC wording: "Verified that a HIGH+ advisory against a transitive Maven dependency opens the tracking issue." That single word closes the biggest hole.security,devops,P1-high).Open Decisions (none)
📋 "Elicit" — Requirements Engineer
Observations
needs-discussionlabel is correctly applied.Recommendations
workflow_dispatchagainst a transitive vuln.Open Decisions
🎨 Leonie — UX & Interaction
No concerns from my angle. This is pure CI / dependency-scanning infrastructure with no user-facing surface — the only "UI" is a Gitea tracking issue, and the team conventions for that (fixed title marker,
security/devops/P1-highlabels, runbook inci-gitea.md) are already covered by the other personas. Nothing to add on interaction or accessibility.🗳️ Decision Queue — Action Required
3 decisions need your input before implementation starts.
Architecture / Sequencing
.gitea/actions/sca-tracking-issue; (b) #820 builds the logic inline now with #818 as a named future adopter; (c) build the shared composite action up front against both. Cost: (a) serializes the two issues but extracts a real abstraction; (b) ships #820 independently but risks two near-duplicate implementations until someone reconciles them; (c) designs the interface against one real caller and one imagined one (Markus's "phantom" risk). (Raised by: Tobias, Markus, Elicit)Scanner choice (the issue's stated Open Decision, refined)
osv-scannervs OWASPdependency-check— and how to feed it the transitive tree. Recommendation stands:osv-scanner(same OSV.dev feed as Renovate, no NVD cache/key, static binary). But because Maven has no lockfile,osv-scanneronly reliably covers the transitive Spring Boot/Hibernate/Jetty tree if you scan a generated CycloneDX SBOM (cyclonedx-maven-plugin) rather than a barepom.xml. Two corollaries the issue didn't budget for:osv-scannerhas no native HIGH-only gate (post-process CVSS ≥ 7.0 yourself injq), whereasdependency-checkhas native transitive resolution andfailBuildOnCVSSbut drags a multi-GB NVD cache. Net: chooseosv-scanner + SBOMunless you specifically want NVD CVSS detail. (Raised by: Tobias, Nora, Markus, Felix)Process