ci: gate releases on a vulnerability scan before anything goes public - #280
Merged
Conversation
Adopts the shared supply-chain workflows and, more importantly, puts a scan in front of every public step. Release pipeline is now: release-please -> build -> security-gate -> publish -> sbom build produces the jar and the SBOM and publishes nothing. security-gate scans the jar that is about to ship and fails on CRITICAL or HIGH. publish only runs when the gate is green, so a vulnerable build can no longer reach the GitHub release, Hangar or Modrinth. publish deliberately does not use always(), which would have run it despite a failed gate. The gate scans the built artifact with rootfs, not the source tree with fs. Measured on this repository: trivy fs source tree -> 0 packages, 0 findings trivy fs shaded jar -> 0 packages, 0 findings trivy rootfs shaded jar -> 12 packages, 1 HIGH A gate on either fs variant would have been green while checking nothing. That one HIGH is real and is fixed here: CVE-2023-5072 in org.json below 20231013, pulled in transitively by club.minnced:discord-webhooks, which still resolves 20230618. A dependency constraint forces 20231013; after it the same scan reports 12 packages and no findings. Without the fix the new gate would block every release on day one. The Dependency-Track upload moves out of the publish job into the shared sbom-publish workflow, so an unreachable server or a rejected API key can no longer take the release with it - which is exactly what happened to 2.9.0. security.yml adds continuous monitoring on top: main plus a weekly sweep, report-only, findings in code scanning. No pull_request trigger, because fork PRs get a read-only token and the SARIF upload would fail for exactly the contributions that most need review.
Contributor
Unit Test Results 8 files 8 suites 1s ⏱️ Results for commit 94c8d04. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on OneLiteFeatherNET/workflows#23 — do not merge before that is released as
v2.6.0, the twouses:pins point at that tag.The pipeline now breaks before anything is public
buildproduces the jar and the SBOM and publishes nothing.security-gatescans the jar that is about to ship and fails on CRITICAL or HIGH.publishruns only when the gate is green, so a vulnerable build can no longer reach the GitHub release, Hangar or Modrinth.publishdeliberately does not usealways()— that would have run it regardless of a failed gate. It uses!cancelled() && needs.security-gate.result == 'success' && (…)instead.The gate scans the artifact, not the source tree
This is the part that would have been easy to get wrong. Measured on this repository:
trivy fson the source treepom.xmlfrom build caches)trivy fson the shaded jartrivy rootfson the shaded jarA gate on either
fsvariant would have reported green while checking nothing at all. Gradle without agradle.lockfilegivesfsnothing to read, and Trivy'sfsscanner does not look inside JARs.That one HIGH is real, and is fixed here
CVE-2023-5072—org.jsonbelow20231013can be driven into a parser denial of service. It reaches our shaded jar transitively:Fixed with a dependency constraint forcing
20231013. Verified end to end:Without this the new gate would block every single release on day one.
SBOM upload moves out of the release job
The Dependency-Track upload now runs as its own job via the shared
sbom-publish.yml, fed the CycloneDX artifact the build produced. An unreachable server or a rejected API key can no longer take the release with it — which is exactly what happened to 2.9.0, where one rejected platform version cost the JAR, both platform releases and the SBOM at once.Note this does not fix the
403that 2.9.0's SBOM upload hit. That is a Dependency-Track API key permission: withautocreateon, the key's team needsPROJECT_CREATION_UPLOADin addition toBOM_UPLOAD. Still needs doing on the server side.security.yml— continuous monitoring on topThe gate only sees a release at the moment it is cut. Most CVEs are published against code that has not changed since, so
security.ymlscansmainplus a weekly sweep, report-only, with findings in code scanning.No
pull_requesttrigger on purpose: fork PRs get a read-only token, so the SARIF upload would fail for exactly the contributions that most need review.Cost
publishre-runs Gradle for the Hangar and Modrinth tasks, since those are Gradle tasks and cannot consume a downloaded jar. With thesetup-gradlecache that is a cache-hit rebuild, and it buys the guarantee that nothing is published before the gate.