From f281c624fc9a2d81d0362cd81abefa96f6cf4047 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Fri, 22 May 2026 07:46:56 +0200 Subject: [PATCH] ci(security): migrate trivy job to security-container reusable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the inline Trivy + SARIF-upload steps with a call to netresearch/.github/.github/workflows/security-container.yml@main (Phase 2.5 extended the reusable with tolerate-pull-failure for exactly this caller). The matrix axis (latest, rolling), workflow_run conclusion gate, and permissions stay on the caller job. tolerate-pull-failure: true on the rolling cell replaces the previous job-level `continue-on-error: ${{ matrix.tag == 'rolling' }}` — GitHub forbids that key on reusable-caller jobs, so the reusable handles the missing-rolling-image case via a pre-flight `docker manifest inspect` probe and skips the Trivy + SARIF-upload steps when the manifest is unavailable (without swallowing legitimate finding-based failures). Defaults of the reusable match the previous inline values (exit-code: 0, ignore-unfixed, vuln-type os,library, severity HIGH,CRITICAL, limit-severities-for-sarif), so behaviour is preserved. osv-scanner stays inline: the composer.lock extraction (docker create + docker cp from /var/www/html) is snipe-it-specific and doesn't generalise into the container reusable surface. build.yml stays inline for now: see PR body for the two upstream gaps that need fixing first (tolerate-build-failure input + a path for caller-resolved ref / caller-computed tag fan-out without a 2-job matrix dance). security.yml: 135 → 118 lines (trivy block 50 → 26). Signed-off-by: Sebastian Mendel --- .github/workflows/security.yml | 67 +++++++++++++--------------------- 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 2b5dbac..9d73382 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -4,13 +4,16 @@ # CVE scanning for the published snipe-it-php-fpm images. # # Runs: -# - Trivy container scan against :latest and :rolling (HIGH/CRITICAL → fail, -# SARIF uploaded to GitHub code-scanning) +# - Trivy container scan against :latest and :rolling — delegated to +# the reusable security-container.yml in netresearch/.github +# (HIGH/CRITICAL → SARIF, exit-code 0 = informational). # - osv-scanner against the composer.lock baked into each image -# (CRITICAL → fail, lower severities informational) +# (informational; stays inline because composer.lock extraction is +# snipe-it-specific and not part of the container reusable surface). # -# Scope is intentionally limited to vulnerability scanning. Scorecard, cosign -# signing/verification, and SBOM attestation live in separate workflows. +# Scope is intentionally limited to vulnerability scanning. Scorecard, +# cosign signing/verification, and SBOM attestation live in separate +# workflows. name: security @@ -40,52 +43,32 @@ env: jobs: trivy: - name: trivy (${{ matrix.tag }}) # On workflow_run, only run if the upstream build actually succeeded. + # `rolling` may not exist in ghcr.io (rolling builds can fail when + # upstream Snipe-IT's composer.json references a CVE-blocked major). + # The reusable's `tolerate-pull-failure` input handles the missing- + # image case via a pre-flight `docker manifest inspect` probe — when + # the manifest is unavailable, the Trivy + SARIF-upload steps are + # skipped and the job exits green. Job-level `continue-on-error:` is + # forbidden by GitHub on reusable-caller jobs, which is why this knob + # lives inside the reusable. if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }} - runs-on: ubuntu-latest - # `:rolling` may not exist in ghcr.io (rolling builds fail when upstream - # Snipe-IT's composer.json references a CVE-blocked major). Don't let - # missing-image errors fail the whole security workflow. - continue-on-error: ${{ matrix.tag == 'rolling' }} + name: trivy (${{ matrix.tag }}) permissions: contents: read + packages: read # pull from ghcr.io (private/org-locked packages) security-events: write # SARIF upload to GitHub code-scanning strategy: fail-fast: false matrix: - # `latest` = pinned-deps release line - # `rolling` = rolling-deps line (catches transitive CVEs) + # `latest` = pinned-deps release line (canonical) + # `rolling` = rolling-deps line (catches transitive CVEs; may be absent) tag: [latest, rolling] - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Trivy — vulnerability scan (HIGH/CRITICAL, SARIF, informational) - uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 - with: - image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ matrix.tag }} - format: sarif - output: trivy-${{ matrix.tag }}.sarif - severity: 'HIGH,CRITICAL' - # exit-code: 0 — informational. CVEs in transitive Snipe-IT deps - # (e.g. phpseclib, onelogin/php-saml, robrichards/xmlseclibs in - # v8.5.0's composer.lock) can't be fixed downstream; gating CI on - # them means CI is permanently red. Operators consume findings via - # GitHub Security tab + the daily-cron alert pattern. - exit-code: '0' - ignore-unfixed: 'true' - limit-severities-for-sarif: 'true' - vuln-type: 'os,library' - - - name: Upload SARIF to code-scanning - if: always() - uses: github/codeql-action/upload-sarif@bc0b696b4103f5fe60f15749af68a046868d511a # codeql-bundle-v2.25.4 - with: - sarif_file: trivy-${{ matrix.tag }}.sarif - category: trivy-${{ matrix.tag }} + uses: netresearch/.github/.github/workflows/security-container.yml@main + with: + image-ref: ghcr.io/${{ github.repository_owner }}/snipe-it-php-fpm:${{ matrix.tag }} + sarif-category: trivy-${{ matrix.tag }} + tolerate-pull-failure: ${{ matrix.tag == 'rolling' }} osv-scanner: # Same gate as trivy — only run on workflow_run if build succeeded.