Files
werkregie/doc/prs/2026-10-13-PR#59-dependency-checks.md
claude-code 5fdfd84e29
werkator build succeeded after 01:00
werkator/dependencies build succeeded after 04:22
werkregie checks its dependencies for vulnerabilities and licenses (#59)
Co-authored-by: claude-code <claude-code@javagil.de>
2026-10-13 13:50:43 +02:00

4.3 KiB

PR#59: werkregie Checks Its Dependencies for Vulnerabilities and Licenses

WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR documentation is not maintained along with new PRs; treat it as a snapshot.

  • PR#58, the required checks of all werk repositories.

Problem

Since PR#58, each werk repository keeps a criticality and risk analysis and checks its dependencies for known vulnerabilities and for their licenses. werkregie has neither.

Non-Goals

  • No mutation tests; they follow in a PR of their own, as a nightly build.
  • No vulnerability check of PR branches yet; see "Follow-up PRs".
  • No security headers, e.g. CSP or HSTS; the risk analysis lists them as an open risk.

Specification

Feature: the build checks the dependencies

Scenario#59.01: a vulnerable dependency fails the build

… so that no dependency with a known vulnerability from CVSS 4 reaches main.

  • Given a dependency with a known vulnerability of CVSS 4 or more, and no suppression for it
  • When Werkator checks main for vulnerabilities
  • Then the check fails and names the vulnerability
Verified by
  • Manual: ./gradlew dependencyCheckAnalyze in dashboard/; its first run failed on CVE-2026-53914 of kotlin-reflect 2.3.20.
  • Werkator's build dependencies on main after the merge.

Scenario#59.02: a dependency with a license outside the allowlist fails the build

… so that no license reaches main without the consent of the human.

  • Given a dependency whose license is not on the allowed list
  • When the build runs
  • Then the build fails and names the dependency and its license
Verified by
  • Manual: ./gradlew checkLicense in dashboard/; with the allowlist emptied, it failed and named all 119 dependencies.

Solution

  • doc/risk-analysis.md: what werkregie handles, who reaches it, what a breach would cost; criticality high, set by the human, so a vulnerability from CVSS 4 fails the build.
  • OWASP Dependency-Check, plugin org.owasp.dependencycheck, task dependencyCheckAnalyze:
    • failBuildOnCVSS 4, over runtimeClasspath, i.e. what ships;
    • the NVD API key comes from the environment variable NVD_API_KEY, else from the Gradle property OWASP_API_KEY;
    • the data counts as current for 24 hours and lies in the Gradle home, shared by the builds on mih09;
    • a suppression needs its reason, in dashboard/config/dependency-check-suppressions.xml;
    • Sonatype's OSS Index is off, since it answers only with credentials.
  • Werkator runs it as the build dependencies on each push to main, with the status werkator/dependencies:
    • the first download of the NVD data takes longer than the 10 min of the build default, so it is a build of its own, with the host's longest timeout;
    • only main for now: Werkator takes statusContext from main, so a build that only this branch defines would report as werkator and overwrite the status of default.
  • The license check, plugin com.github.jk1.dependency-license-report, in check, against the allowlist of the shared workflow in dashboard/config/allowed-licenses.json.
  • .werkrevisor.toml marks the suppressions, the allowlist and the risk analysis as hard paths; the host's paths for secrets stay.
  • AGENTS.md names the commands of the checks.
  • Version 0.38.0.

Additional Changes

  • kotlin-reflect 2.4.21 instead of 2.3.20, which Exposed brings under Trixnity: the first check found CVE-2026-53914, CVSS 9.8, in Kotlin before 2.4.20.

  • MIT-0 joins the allowed licenses of the shared workflow, chosen by the human: reactive-streams, which Trixnity's storage needs, is under MIT-0, which is more permissive than MIT.

Follow-up PRs

  • The build dependencies for each PR branch. Werkator reads a build's statusContext from main, never from the branch it builds. So a PR branch can run dependencies under its own context only after this PR has brought the context to main; before, its result would overwrite the status werkator of the build default on the same commit. The follow-up drops branches: ["main"], and the human then adds werkator/dependencies to the required checks of main.
  • Mutation tests with werkcrappit in a nightly build.