Repository navigation
Add Trivy vulnerability scanning for Docker images - #854
Conversation
Scan the freshly built images (default and alpine) in build.yml for fixable CRITICAL/HIGH CVEs, and add a weekly docker-scan workflow that scans the published openidentityplatform/opendj:latest/:alpine images and uploads SARIF reports to the Security tab.
Switch the build.yml image scans from table output with exit-code 1 to SARIF uploaded via codeql-action, so PRs get a "Code scanning results" check like CodeQL instead of a failed docker job. Categories trivy-build-default/trivy-build-alpine are distinct from the weekly docker-scan.yml ones, which track the published images.
maximthomas
left a comment
There was a problem hiding this comment.
Nice direction — the repo had no image scanning at all, and this already caught 4 real HIGH findings (jackson-databind, mssql-jdbc) that now have fix branches. Switching CI to code scanning in 1ec4c496 was the right call.
Two things to fix before merge, both small. Reviewed at 1ec4c496.
severity: CRITICAL,HIGH is silently ignored for SARIF (major)
The pinned action unsets the filter whenever the format is SARIF, unless you opt back in — entrypoint.sh at ed142fd:
if [ "${TRIVY_FORMAT:-}" = "sarif" ]; then
if [ "${INPUT_LIMIT_SEVERITIES_FOR_SARIF:-false,,}" != "true" ]; then
echo "Building SARIF report with all severities"
unset TRIVY_SEVERITYSo every UNKNOWN/LOW/MEDIUM finding from both base images and the ~130 bundled jars lands in the Security tab and on the PR's code-scanning check. This got wider in 1ec4c496: .github/workflows/build.yml moved from format: table (where severity works) to format: sarif (where it doesn't), so all four scan steps are now affected — .github/workflows/build.yml:473, :614, and .github/workflows/docker-scan.yml:41 (both matrix legs).
Add to all four steps:
severity: CRITICAL,HIGH
limit-severities-for-sarif: trueNote it must be exactly lowercase true — ${INPUT_LIMIT_SEVERITIES_FOR_SARIF:-false,,} above is a literal-string default, not bash lowercasing, so True/TRUE silently won't match.
Trivy's cache will evict the Maven caches (major)
trivy-action bundles actions/cache internally (cache input defaults to true):
- if: ${{ inputs.cache == 'true' }}
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ${{ inputs.cache-dir }}
key: cache-trivy-${{ steps.date.outputs.date }}It stores trivy-db plus trivy-java-db — the java-db is unavoidable here, since many bundled jars are renamed and lack META-INF/maven/**/pom.properties, so SHA1→GAV lookup is required. That's roughly 1 GB saved per branch scope per day.
The repo is already at the ceiling:
$ gh api repos/OpenIdentityPlatform/OpenDJ/actions/cache/usage
active_caches_size_in_bytes: 10899282214 # 10.15 GiB, limit is 10 GB
active_caches_count: 31
The six largest entries are ~1.07 GiB Linux-m2-repository-* caches that the 9-job build-maven matrix depends on. New cache-trivy-* entries will LRU-evict them and Maven will re-download its tree.
This is currently masked: actions/cache saves with post-if: success(), and the Trivy step was failing, so the save was skipped. Removing exit-code in 1ec4c496 activates it. Simplest fix on all four steps:
cache: falseOr adopt the action's scheduled master-branch cache-refresh pattern with TRIVY_SKIP_DB_UPDATE / TRIVY_SKIP_JAVA_DB_UPDATE.
TRIVY_INSECURE disables TLS globally (minor)
.github/workflows/build.yml:483 and :624:
env:
# the registry service on localhost:5000 is plain HTTP
TRIVY_INSECURE: "true"insecure is a global trivy flag, not per-target — it sets the process-wide HTTP transport's InsecureSkipVerify and is passed to RegistryOpts() on the vulnerability-DB fetch, so the trivy-db/trivy-java-db download stops verifying certificates.
It also isn't needed. go-containerregistry already returns http unconditionally for localhost:-prefixed names, and trivy resolves the image from the local Docker daemon anyway (--image-src defaults to docker,containerd,podman,remote, and the earlier docker run steps already pulled it) — the registry is never contacted. Safe to drop all three lines from both jobs.
Nits
- Only
linux/amd64is scanned: because trivy resolves via the Docker daemon, the other published arches aren't covered. Notablyopendj-packages/opendj-docker/Dockerfile-alpineships a JDK 11 runtime forlinux/386(if [ "$TARGETARCH" = "386" ]) — the one arch that genuinely differs is the one skipped. Worth a comment noting the limitation. - The alpine scan is near-redundant: both images unpack the same
opendj-*.zip, and the failing run produced identical jar findings with 0 OS findings on each side. It roughly doubles DB traffic and cache pressure for little added coverage. ignore-unfixed: truein.github/workflows/docker-scan.yml:48: reasonable on a gate, but this workflow exists to surface CVEs in already-released images. Unpatched criticals are exactly what you'd want to see there in order to publish an advisory or pull a tag.- Upload steps lack
if: always(): if Trivy errors (rate limit, DB fetch), no SARIF is uploaded and the category keeps showing the previous run's results. If you add it, pair withhashFiles()so a missing file doesn't turn into a second error. scannersdefaults tovuln,secret: harmless now that nothing gates on exit code, but secret findings will appear in the SARIF under a step named "Scan image for vulnerabilities".scanners: vulnalso speeds up the scan.- Duplication: the Trivy + upload block is byte-identical across
build-dockerandbuild-docker-alpineapart from the tag suffix and category, as is the newimage_repositoryexport. Fine for now, but each of the fixes above has to be applied four times. image_repository:${GITHUB_REPOSITORY,,}is already inlined at nine sites in the same file, andsteps.meta.outputs.tagsalready holds the exact ref.
Checked and fine, for the record: the ed142fd pin matches v0.36.0 exactly; upload-sarif@v4 exists and matches .github/workflows/codeql.yml; all four categories are distinct and don't collide; no cron conflict with CodeQL (27 3 * * 1 vs 30 5 * * 1); the image-ref does resolve to the freshly built image despite release_version naming; and fork PRs upload SARIF fine despite the read-only token — verified on this PR's own CodeQL run, which logged SecurityEvents: read and still uploaded successfully.
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
…Y_INSECURE severity is silently unset for SARIF output unless limit-severities-for-sarif: true is also passed; the action's built-in ~1GB DB cache would evict the m2-repository caches out of the repo's 10GB quota; TRIVY_INSECURE globally disables TLS verification (including the DB download) and is unnecessary - the image is resolved from the local Docker daemon. Also scan vulnerabilities only (no secret scanner), report unfixed CVEs in the published-image scans, and guard the SARIF uploads with always() + hashFiles.
|
Thanks for the thorough review — both majors and most of the nits are addressed in 290d6e4:
Left as-is for now:
PR description updated to match. Great catch on the |
Scans the freshly built images (default and alpine) in build.yml for fixable CRITICAL/HIGH CVEs and uploads SARIF to code scanning, and adds a weekly docker-scan workflow that scans the published openidentityplatform/openicf:latest/:alpine images. Mirrors OpenIdentityPlatform/OpenDJ#854, with aquasecurity/trivy-action pinned by commit SHA to match the pinning convention introduced in OpenIdentityPlatform#130.
Scans the freshly built images (default and alpine) in build.yml for fixable CRITICAL/HIGH CVEs and uploads SARIF to code scanning, and adds a weekly docker-scan workflow that scans the published openidentityplatform/openicf:latest/:alpine images. Mirrors OpenIdentityPlatform/OpenDJ#854, with aquasecurity/trivy-action pinned by commit SHA to match the pinning convention introduced in OpenIdentityPlatform#130.
- build.yml: scan the freshly built default and alpine images after the docker tests and upload the SARIF report to code scanning (fixable CRITICAL/HIGH only); the two docker jobs get security-events: write - docker-scan.yml: weekly (and manual) scan of the published openidentityplatform/openidm:latest and :alpine images, unfixed CVEs included, reported under separate trivy-image-* categories Ported from OpenIdentityPlatform/OpenDJ#854.
- build.yml: scan the freshly built default and alpine images after the docker tests and upload the SARIF report to code scanning (fixable CRITICAL/HIGH only); the two docker jobs get security-events: write - docker-scan.yml: weekly (and manual) scan of the published openidentityplatform/openidm:latest and :alpine images, unfixed CVEs included, reported under separate trivy-image-* categories Ported from OpenIdentityPlatform/OpenDJ#854.
- build.yml: scan the freshly built default and alpine images after the docker tests and upload the SARIF report to code scanning (fixable CRITICAL/HIGH only); the two docker jobs get security-events: write - docker-scan.yml: weekly (and manual) scan of the published openidentityplatform/openidm:latest and :alpine images, unfixed CVEs included, reported under separate trivy-image-* categories Ported from OpenIdentityPlatform/OpenDJ#854.
Adds Docker image vulnerability scanning with [Trivy](https://github.com/aquasecurity/trivy), mirroring OpenIdentityPlatform/OpenDJ#854. **`build.yml`** — `build-docker` and `build-docker-alpine` now build the image from the ZIP of the commit under test, as OpenDJ does: they wait for `build-maven`, take `openicf-*.zip` from its `ubuntu-latest-11` artifact and uncomment the `COPY` in the Dockerfile. Before, the image was built from the last release's ZIP (`VERSION=release_version`), so neither the smoke test nor a scan saw this commit's jars. `Dockerfile` / `Dockerfile-alpine` now download the release ZIP only when none was copied in, so `release.yml` is unchanged. They run unless the run was cancelled (`if: ${{ !cancelled() }}`), so a red matrix leg other than `ubuntu-latest-11` no longer skips the image smoke test and scan; a red `ubuntu-latest-11` leg fails them at the artifact download. A docker-job re-run after the artifact's 5-day retention needs "Re-run all jobs". Both jobs scan the freshly built image (resolved from the local Docker daemon, so the runner's `linux/amd64` manifest only) right after the Docker test step. Findings do not fail the build: the SARIF report is uploaded via `codeql-action/upload-sarif`, so PRs get a `Trivy` code-scanning check next to `CodeQL` (one check-run per SARIF tool, covering both the `trivy-build-default` and `trivy-build-alpine` categories), and the full list lives in the Security tab. The scan step is `continue-on-error`, so a Trivy or trivy-db registry outage does not fail the image smoke test. Only fixable CRITICAL/HIGH CVEs are reported (`ignore-unfixed: true` plus `limit-severities-for-sarif: true` — without the latter the severity filter is silently dropped for SARIF output), and only the vulnerability scanner runs (`scanners: vuln`). The action's built-in ~1GB DB cache is disabled (`cache: false`) so it cannot evict the `m2-repository` caches out of the repo's 10GB actions-cache quota. Because trivy-action passes that same `cache` input to `setup-trivy`, which would then skip its binary cache and do an anonymous github.com release lookup in every job, Trivy is installed by a separate `aquasecurity/setup-trivy` step (`version: v0.70.0`, `cache: true`, also `continue-on-error`) and trivy-action runs with `skip-setup-trivy: true`. The two docker jobs get `security-events: write` next to `contents: read`; build.yml's workflow-level `permissions:` (from #130) stays `contents: read` for every other job. **`docker-scan.yml`** (new) — weekly cron (`30 5 * * 1`) + `workflow_dispatch` scan of the published `openidentityplatform/openicf:latest` and `:alpine` images: new CVEs surface in already-released images (mostly via the base image) without any change in this repository. Unlike the build-time scan, unfixed CVEs are reported too. Trivy is installed the same way (separate `setup-trivy` step with the binary cached, here without `continue-on-error`). Only the `linux/amd64` manifest is scanned (no `--platform` is passed, so go-containerregistry's default applies); the other published platforms are not (`alpine` on `linux/386` ships `openjdk11-jre` instead of `openjdk25-jre`). Reports are uploaded as SARIF with a separate category per tag (`trivy-image-*`, distinct from the `trivy-build-*` categories in `build.yml`). The scheduled run is skipped in forks; manual runs are always allowed. Its triggers need the file on the default branch, so nothing on this PR runs it: it is to be started once by hand (`gh workflow run docker-scan.yml`) right after merge. Since the `trivy-image-*` categories exist only on master, every PR that uploads both `trivy-build-*` categories will then get a `Trivy` check that concludes neutral with "2 configurations not found" (as on OpenDJ). `aquasecurity/trivy-action` (v0.36.0) and `aquasecurity/setup-trivy` (v0.2.6, the same SHA trivy-action itself uses) are pinned by commit SHA, matching the pinning convention #130 introduced for third-party actions in this repo. Future false positives / accepted findings can be suppressed via a `.trivyignore` file in the repository root or dismissed in the Security tab.
* Add Trivy vulnerability scanning for Docker images - build.yml: scan the freshly built default and alpine images after the docker tests and upload the SARIF report to code scanning (fixable CRITICAL/HIGH only); the two docker jobs get security-events: write - docker-scan.yml: weekly (and manual) scan of the published openidentityplatform/openidm:latest and :alpine images, unfixed CVEs included, reported under separate trivy-image-* categories Ported from OpenIdentityPlatform/OpenDJ#854. * Build the docker images from the commit's zip for the Trivy scan - build-docker / build-docker-alpine: need build-maven, download the ubuntu-latest-25 openidm zip and uncomment the Dockerfile COPY; drop the VERSION build-arg, with which the Dockerfiles replace the copied zip with the latest release, so the docker test and the scan cover this commit's own dependencies - continue-on-error on the Trivy scan and SARIF upload steps, so a scanner download failure does not fail the docker jobs - docker-scan.yml: note that only the linux/amd64 manifest is scanned * Fail the docker test on a release image and run it despite unrelated matrix failures - build-docker / build-docker-alpine: the Docker test checks that the image's bundles carry the version of this commit's zip, so a restored VERSION build-arg (the Dockerfiles then replace the copied zip with the release) fails the step instead of scanning the release again - if: !cancelled() on both docker jobs: they consume only the ubuntu-latest-25 artifact, so a red leg elsewhere in the build-maven matrix no longer skips the docker test and the trivy-build-* upload; a red ubuntu-latest-25 leg fails the artifact download - docker-scan.yml: the build.yml scan is not a gate, findings never fail the build * Capture the bundle listing before grepping it in the docker test "ls | grep -q" under pipefail fails the step with exit 141 whenever grep exits on the first match before ls has finished writing.
build.yml: scan the freshly built image in build-docker right after the docker tests and upload the SARIF report to code scanning (category trivy-build-default). Only fixable CRITICAL/HIGH CVEs are reported and findings do not fail the build. docker-scan.yml (new): weekly scan of the published openidentityplatform/openig:latest image, unfixed CVEs included, so that new CVEs in the released image surface without a change in this repo. Same setup as OpenIdentityPlatform/OpenDJ#854: severity filter kept for SARIF via limit-severities-for-sarif, trivy DB cache disabled to keep the ~1GB DBs from evicting the m2-repository caches, action pinned by SHA.
Adds Docker image vulnerability scanning with [Trivy](https://github.com/aquasecurity/trivy), the same setup as OpenIdentityPlatform/OpenDJ#854 in its final, reviewed form, plus what the later sibling PRs settled (OpenIdentityPlatform/OpenICF#135, OpenIdentityPlatform/OpenIDM#216): a separately cached Trivy install and a scan that cannot fail the build. **`build.yml`** — `build-docker` now scans the freshly built image (resolved from the local Docker daemon, so the runner's `linux/amd64` manifest only) right after the docker test. The image is built from this commit's `OpenIG-*.war` (the `ubuntu-latest-11` artifact; the Dockerfile downloads the release war only when none was copied in), so the scan covers this commit's dependencies plus the `tomcat` base image. The SARIF report is uploaded via `codeql-action/upload-sarif` and the full list lives in the Security tab. Only fixable CRITICAL/HIGH CVEs are reported (`ignore-unfixed: true` plus `limit-severities-for-sarif: true` — without the latter the severity filter is silently dropped for SARIF output), and only the vulnerability scanner runs (`scanners: vuln`). - **The scan never fails the build.** Findings exit 0 (no `exit-code`), and the Install and Scan steps are `continue-on-error`, so a Trivy or trivy-db registry outage cannot turn `build-docker` red — a red Build would also skip the snapshot deploy in `deploy.yml`. The scan step has `timeout-minutes: 15`: trivy's own `--timeout` does not bound the Java DB download. The upload runs on `!cancelled()` and only when a report exists. - **Caching.** The action's built-in DB cache is disabled (`cache: false`): it saves the ~1 GB trivy DBs per branch scope and day, which would fill the repo's 10 GB actions-cache quota within days and LRU-evict the `m2-repository` caches (350–480 MiB each) the `build-maven` matrix depends on. The cache stood at 10.12 GiB when this PR was opened. `cache: false` would also skip setup-trivy's binary cache and leave an anonymous github.com release lookup in every run (it lost a scan in OpenICF#135), so Trivy `v0.70.0` (trivy-action v0.36.0's default) is installed by its own `aquasecurity/setup-trivy` step with `cache: true`, and trivy-action runs with `skip-setup-trivy: true`. - **The PR check.** GitHub creates one `Trivy` check-run per SARIF tool, next to `CodeQL`; `trivy-build-default` is only the SARIF category. That check is diff-scoped: image-path alerts never fall inside a PR diff, so it reports "No new alerts in code changed by this pull request" whatever the scan found. The findings for a PR are in the Security tab under `pr:<N> tool:Trivy`. - The job gets `contents: read` + `security-events: write`; SARIF upload also works for fork PRs on public repositories. `actions/checkout@v7` (with `persist-credentials: false`) is added as the first step of the job, as in OpenDJ, so `upload-sarif` has a git checkout to work from (before `download-artifact`, since checkout cleans the workspace). **`docker-scan.yml`** (new) — weekly cron (`30 5 * * 1`, no overlap with CodeQL's `27 3 * * 1`) + `workflow_dispatch` scan of the published `openidentityplatform/openig:latest` image: new CVEs surface in the already-released image (mostly via the `tomcat` base image) without any change in this repository. Unlike the build-time scan, unfixed CVEs are reported too — surfacing them in a released image is the point. Report category `trivy-image-latest`, distinct from `trivy-build-default`. The scheduled run is skipped in forks; manual runs are always allowed. OpenIG publishes a single image variant, so the matrix has one tag. - Only the `linux/amd64` manifest is scanned (no `--platform` is passed); the other published platforms (arm64, ppc64le, s390x) are not. - No `continue-on-error` here: a failed weekly scan is the signal. The job has `timeout-minutes: 30`, for the same reason as the build-time step. - Once it has run, `trivy-image-latest` exists only on master, so every PR's `Trivy` check concludes **neutral** with "1 configuration not found" (as on OpenAM PRs today). This is documented in the workflow header. - Its triggers need the file on the default branch, so it cannot run on this PR. I will start it by hand (`gh workflow run docker-scan.yml`) right after merge and report the run here. `aquasecurity/trivy-action` (`ed142fd…`, v0.36.0 — the current latest) and `aquasecurity/setup-trivy` (`81e5143…`, v0.3.1) are pinned by commit SHA; `actions/*` and `github/codeql-action` stay on major tags, the policy of #170. **`.github/dependabot.yml`** (new, second commit) enables the `github-actions` ecosystem so the pinned SHAs keep getting updated: weekly, one grouped PR for all actions, labels `ci` + `dependencies`. It is byte-identical to the file added in #170, so whichever PR lands first brings it in without a conflict. Not used: `TRIVY_INSECURE` (global TLS off, and not needed — the image is read from the daemon, the registry is never contacted). Overlap with #170: both touch `build.yml`, in different hunks (#170: workflow-level `permissions` and the `uses:` pins; here: the `build-docker` job header, its first step and its tail). The added license header is the identical block, so it should merge cleanly either way. This PR's own `build-docker` run exercises the new build-time steps.
Adds Docker image vulnerability scanning with Trivy:
build.yml— bothbuild-dockerandbuild-docker-alpinejobs now scan the freshly built image (resolved from the local Docker daemon, so the runner'slinux/amd64manifest only) right after the functional docker tests. Findings do not fail the build: the SARIF report is uploaded viacodeql-action/upload-sarif, so PRs get a "Code scanning results / trivy-build-*" check like CodeQL and the full list lives in the Security tab. Only fixable CRITICAL/HIGH CVEs are reported (ignore-unfixed: truepluslimit-severities-for-sarif: true— without the latter the severity filter is silently dropped for SARIF output), and only the vulnerability scanner runs (scanners: vuln). The action's built-in ~1GB DB cache is disabled (cache: false) so it cannot evict them2-repositorycaches out of the repo's 10GB actions-cache quota. The two docker jobs getsecurity-events: write; SARIF upload also works for fork PRs on public repositories.docker-scan.yml(new) — weekly cron (30 5 * * 1) +workflow_dispatchscan of the publishedopenidentityplatform/opendj:latestand:alpineimages: new CVEs surface in already-released images (mostly via the base image) without any change in this repository. Unlike the build-time scan, unfixed CVEs are reported too — surfacing them in released images is the point. Reports are uploaded as SARIF to the Security tab with a separate category per tag (trivy-image-*, distinct from thetrivy-build-*categories inbuild.yml). The scheduled run is skipped in forks; manual runs are always allowed.aquasecurity/trivy-actionis pinned by commit SHA (v0.36.0), matching the pinning style of the other third-party actions inbuild.yml. Future false positives / accepted findings can be suppressed via a.trivyignorefile in the repository root or dismissed in the Security tab.