From 7c660aae499de5b39db68648bbc13a5cbc597c64 Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 7 Aug 2026 15:01:39 -0700 Subject: [PATCH 01/10] fix(ci): optimize Trivy analysis workflow with path triggers, dynamic caching, and secret gates --- .github/workflows/analysis.yml | 36 ++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index aff5fcfa3..3a9533f58 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -98,25 +98,49 @@ jobs: steps: - uses: actions/checkout@v7 + - id: diff + uses: bcgov/actions/diff-triggers@main + with: + triggers: | + backend/** + frontend/** + common/** + package*.json + Containerfile* + compose*.yml + .github/** + - name: Cache Trivy DB + if: steps.diff.outputs.triggered == 'true' uses: actions/cache@v6 with: path: .trivycache - key: ${{ runner.os }}-trivy-v0.36.0-cache-v1 + key: ${{ runner.os }}-trivy-${{ hashFiles('.github/workflows/analysis.yml') }} restore-keys: | ${{ runner.os }}-trivy- - # Reports every severity to the Security tab for triage. Findings are not a merge - # gate: advisories are published on their own schedule, not the contributor's, and - # committed secrets are already blocked at push time by GitHub secret scanning. + - name: Scan for secrets and misconfigurations + if: steps.diff.outputs.triggered == 'true' + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + ignore-unfixed: true + scan-type: "fs" + scanners: "secret,misconfig" + severity: "CRITICAL,HIGH" + trivyignores: ".github/.trivyignore" + cache-dir: .trivycache + skip-dirs: "**/node_modules,**/coverage,**/dist,.git" + exit-code: "1" + - name: Run Trivy vulnerability scanner in repo mode + if: steps.diff.outputs.triggered == 'true' && (success() || failure()) uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: format: "sarif" output: "trivy-results.sarif" ignore-unfixed: true scan-type: "fs" - scanners: "vuln,secret,misconfig" + scanners: "vuln" severity: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL" trivyignores: ".github/.trivyignore" cache-dir: .trivycache @@ -124,7 +148,7 @@ jobs: exit-code: "0" - name: Upload Trivy scan results to GitHub Security tab - if: always() + if: steps.diff.outputs.triggered == 'true' && (success() || failure()) uses: github/codeql-action/upload-sarif@v4 with: category: "trivy" From be0e2b367e859a90a5cca3bdb6107d07333fe505 Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 7 Aug 2026 16:12:54 -0700 Subject: [PATCH 02/10] refactor: consolidate analysis into reusable workflow --- .github/workflows/merge.yml | 6 ++++- .github/workflows/pr-open.yml | 6 ++++- .../{analysis.yml => reusable-analysis.yml} | 24 ++++--------------- .github/workflows/scheduled.yml | 4 ++++ 4 files changed, 19 insertions(+), 21 deletions(-) rename .github/workflows/{analysis.yml => reusable-analysis.yml} (89%) diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index 9985a74a3..cf4dd53d2 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -49,9 +49,13 @@ jobs: with: target: test + analysis: + name: Analysis + uses: ./.github/workflows/reusable-analysis.yml + deploy-prod: name: PROD Deploys (${{ needs.init.outputs.pr }}) - needs: [tests, init] + needs: [tests, init, analysis] uses: ./.github/workflows/reusable-deploy.yml secrets: db_password: ${{ secrets.db_password }} diff --git a/.github/workflows/pr-open.yml b/.github/workflows/pr-open.yml index aa567d7cc..1b1b7ccfc 100644 --- a/.github/workflows/pr-open.yml +++ b/.github/workflows/pr-open.yml @@ -48,6 +48,10 @@ jobs: needs: [deploys] uses: ./.github/workflows/reusable-tests.yml + analysis: + name: Analysis + uses: ./.github/workflows/reusable-analysis.yml + # ========================================================================== # WARNING: This job acts as the required merge gate for this workflow. # If you add a new job to this workflow, you MUST add its ID to the 'needs' @@ -55,7 +59,7 @@ jobs: # ========================================================================== results: name: PR Results - needs: [builds, deploys, tests] + needs: [analysis, builds, deploys, tests] if: always() runs-on: ubuntu-slim timeout-minutes: 1 diff --git a/.github/workflows/analysis.yml b/.github/workflows/reusable-analysis.yml similarity index 89% rename from .github/workflows/analysis.yml rename to .github/workflows/reusable-analysis.yml index 3a9533f58..9fe7f318c 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/reusable-analysis.yml @@ -1,24 +1,13 @@ -name: Analysis +name: Reusable Analysis on: - push: - branches: [main] - pull_request: - types: [opened, reopened, synchronize, ready_for_review, converted_to_draft] - schedule: - - cron: "0 11 * * 0" # 3 AM PST = 12 PM UDT, runs sundays - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + workflow_call: permissions: {} jobs: backend-tests: name: Backend Tests - if: (! github.event.pull_request.draft) runs-on: ubuntu-24.04 timeout-minutes: 5 services: @@ -59,7 +48,6 @@ jobs: frontend-tests: name: Frontend Tests - if: (! github.event.pull_request.draft) runs-on: ubuntu-24.04 timeout-minutes: 5 steps: @@ -86,10 +74,8 @@ jobs: supply_scan: true triggers: ('frontend/') - # https://github.com/marketplace/actions/aqua-security-trivy trivy: name: Trivy Security Scan - if: (! github.event.pull_request.draft) permissions: contents: read security-events: write @@ -115,7 +101,7 @@ jobs: uses: actions/cache@v6 with: path: .trivycache - key: ${{ runner.os }}-trivy-${{ hashFiles('.github/workflows/analysis.yml') }} + key: ${{ runner.os }}-trivy-${{ hashFiles('.github/workflows/reusable-analysis.yml') }} restore-keys: | ${{ runner.os }}-trivy- @@ -159,10 +145,10 @@ jobs: # If you add a new job to this workflow, you MUST add its ID to the 'needs' # array below, otherwise its failure or cancellation will not block the PR! # ========================================================================== - results: + analysis-results: name: Analysis Results needs: [backend-tests, frontend-tests, trivy] - if: always() && (! github.event.pull_request.draft) + if: always() runs-on: ubuntu-slim timeout-minutes: 1 steps: diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml index f05ba9fa5..2afe4316b 100644 --- a/.github/workflows/scheduled.yml +++ b/.github/workflows/scheduled.yml @@ -12,6 +12,10 @@ concurrency: permissions: {} jobs: + analysis: + name: Analysis + uses: ./.github/workflows/reusable-analysis.yml + stale-branches: name: Close Stale Branches & PRs runs-on: ubuntu-slim From 457142ef3beeb469a19f533c0e86fdb5c932f6c2 Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 7 Aug 2026 17:09:30 -0700 Subject: [PATCH 03/10] Update merge.yml Signed-off-by: Derek Roberts --- .github/workflows/merge.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index cf4dd53d2..1c8e18d11 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -17,6 +17,10 @@ concurrency: permissions: {} jobs: + analysis: + name: Analysis + uses: ./.github/workflows/reusable-analysis.yml + init: name: Initialize permissions: @@ -49,13 +53,9 @@ jobs: with: target: test - analysis: - name: Analysis - uses: ./.github/workflows/reusable-analysis.yml - deploy-prod: name: PROD Deploys (${{ needs.init.outputs.pr }}) - needs: [tests, init, analysis] + needs: [tests, init] uses: ./.github/workflows/reusable-deploy.yml secrets: db_password: ${{ secrets.db_password }} From 10d48d0a39a604c2dc0bbb9ee1f759b290e18453 Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 7 Aug 2026 17:11:39 -0700 Subject: [PATCH 04/10] fix(ci): pin diff-triggers action and grant pull-requests read permission --- .github/workflows/reusable-analysis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-analysis.yml b/.github/workflows/reusable-analysis.yml index 9fe7f318c..d79f0bfa1 100644 --- a/.github/workflows/reusable-analysis.yml +++ b/.github/workflows/reusable-analysis.yml @@ -78,6 +78,7 @@ jobs: name: Trivy Security Scan permissions: contents: read + pull-requests: read security-events: write runs-on: ubuntu-slim timeout-minutes: 5 @@ -85,7 +86,7 @@ jobs: - uses: actions/checkout@v7 - id: diff - uses: bcgov/actions/diff-triggers@main + uses: bcgov/actions/diff-triggers@4ad61a784f1c17765b03d8d6de9737c1d3f4c0f2 # v0.5.0 with: triggers: | backend/** From e60c641d3a7dcf5e1a83a9d705ec4b0915436c16 Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 7 Aug 2026 17:20:11 -0700 Subject: [PATCH 05/10] ci(workflows): remove path-based diff triggers and pull-requests permission from Trivy scan --- .github/workflows/reusable-analysis.yml | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/.github/workflows/reusable-analysis.yml b/.github/workflows/reusable-analysis.yml index d79f0bfa1..c7684c5a2 100644 --- a/.github/workflows/reusable-analysis.yml +++ b/.github/workflows/reusable-analysis.yml @@ -78,27 +78,13 @@ jobs: name: Trivy Security Scan permissions: contents: read - pull-requests: read security-events: write runs-on: ubuntu-slim timeout-minutes: 5 steps: - uses: actions/checkout@v7 - - id: diff - uses: bcgov/actions/diff-triggers@4ad61a784f1c17765b03d8d6de9737c1d3f4c0f2 # v0.5.0 - with: - triggers: | - backend/** - frontend/** - common/** - package*.json - Containerfile* - compose*.yml - .github/** - - name: Cache Trivy DB - if: steps.diff.outputs.triggered == 'true' uses: actions/cache@v6 with: path: .trivycache @@ -107,7 +93,6 @@ jobs: ${{ runner.os }}-trivy- - name: Scan for secrets and misconfigurations - if: steps.diff.outputs.triggered == 'true' uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: ignore-unfixed: true @@ -120,7 +105,7 @@ jobs: exit-code: "1" - name: Run Trivy vulnerability scanner in repo mode - if: steps.diff.outputs.triggered == 'true' && (success() || failure()) + if: success() || failure() uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: format: "sarif" @@ -135,7 +120,7 @@ jobs: exit-code: "0" - name: Upload Trivy scan results to GitHub Security tab - if: steps.diff.outputs.triggered == 'true' && (success() || failure()) + if: success() || failure() uses: github/codeql-action/upload-sarif@v4 with: category: "trivy" From 7ed29e33e0bb4fb08e3bc7e593f153e1988e4dda Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 7 Aug 2026 17:22:59 -0700 Subject: [PATCH 06/10] ci(workflows): expand Trivy SARIF scanner to include vuln, secret, and misconfig --- .github/workflows/reusable-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-analysis.yml b/.github/workflows/reusable-analysis.yml index c7684c5a2..d6e97ddd5 100644 --- a/.github/workflows/reusable-analysis.yml +++ b/.github/workflows/reusable-analysis.yml @@ -112,7 +112,7 @@ jobs: output: "trivy-results.sarif" ignore-unfixed: true scan-type: "fs" - scanners: "vuln" + scanners: "vuln,secret,misconfig" severity: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL" trivyignores: ".github/.trivyignore" cache-dir: .trivycache From c69ce7b5602ab22f4fd80770b43801a599ed3fd2 Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 7 Aug 2026 17:24:28 -0700 Subject: [PATCH 07/10] ci(workflows): grant permissions to analysis jobs in caller workflows --- .github/workflows/merge.yml | 3 +++ .github/workflows/pr-open.yml | 3 +++ .github/workflows/scheduled.yml | 3 +++ 3 files changed, 9 insertions(+) diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index 1c8e18d11..2d2fb5a2f 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -19,6 +19,9 @@ permissions: {} jobs: analysis: name: Analysis + permissions: + contents: read + security-events: write uses: ./.github/workflows/reusable-analysis.yml init: diff --git a/.github/workflows/pr-open.yml b/.github/workflows/pr-open.yml index 1b1b7ccfc..ab363bbdf 100644 --- a/.github/workflows/pr-open.yml +++ b/.github/workflows/pr-open.yml @@ -50,6 +50,9 @@ jobs: analysis: name: Analysis + permissions: + contents: read + security-events: write uses: ./.github/workflows/reusable-analysis.yml # ========================================================================== diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml index 2afe4316b..e9d1dc9c6 100644 --- a/.github/workflows/scheduled.yml +++ b/.github/workflows/scheduled.yml @@ -14,6 +14,9 @@ permissions: {} jobs: analysis: name: Analysis + permissions: + contents: read + security-events: write uses: ./.github/workflows/reusable-analysis.yml stale-branches: From bddb7341630b31220edf8ad11b91c7bcb8d1de0c Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 7 Aug 2026 17:28:31 -0700 Subject: [PATCH 08/10] ci(workflows): consolidate Trivy scans and hard gate on critical/high findings --- .github/workflows/reusable-analysis.yml | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/.github/workflows/reusable-analysis.yml b/.github/workflows/reusable-analysis.yml index d6e97ddd5..42c789d1e 100644 --- a/.github/workflows/reusable-analysis.yml +++ b/.github/workflows/reusable-analysis.yml @@ -92,20 +92,7 @@ jobs: restore-keys: | ${{ runner.os }}-trivy- - - name: Scan for secrets and misconfigurations - uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 - with: - ignore-unfixed: true - scan-type: "fs" - scanners: "secret,misconfig" - severity: "CRITICAL,HIGH" - trivyignores: ".github/.trivyignore" - cache-dir: .trivycache - skip-dirs: "**/node_modules,**/coverage,**/dist,.git" - exit-code: "1" - - - name: Run Trivy vulnerability scanner in repo mode - if: success() || failure() + - name: Run Trivy Security Scan uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: format: "sarif" @@ -113,11 +100,11 @@ jobs: ignore-unfixed: true scan-type: "fs" scanners: "vuln,secret,misconfig" - severity: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL" + severity: "CRITICAL,HIGH" trivyignores: ".github/.trivyignore" cache-dir: .trivycache skip-dirs: "**/node_modules,**/coverage,**/dist,.git" - exit-code: "0" + exit-code: "1" - name: Upload Trivy scan results to GitHub Security tab if: success() || failure() From 1f7ce2d929f2e0073b65e094952cf46b23796723 Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 7 Aug 2026 17:30:06 -0700 Subject: [PATCH 09/10] ci(workflows): scan all severities with exit code 0 to report without blocking builds --- .github/workflows/reusable-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-analysis.yml b/.github/workflows/reusable-analysis.yml index 42c789d1e..4a0ff80c8 100644 --- a/.github/workflows/reusable-analysis.yml +++ b/.github/workflows/reusable-analysis.yml @@ -100,11 +100,11 @@ jobs: ignore-unfixed: true scan-type: "fs" scanners: "vuln,secret,misconfig" - severity: "CRITICAL,HIGH" + severity: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL" trivyignores: ".github/.trivyignore" cache-dir: .trivycache skip-dirs: "**/node_modules,**/coverage,**/dist,.git" - exit-code: "1" + exit-code: "0" - name: Upload Trivy scan results to GitHub Security tab if: success() || failure() From d20d126c692b8d7ac915efc76ac2b1e0f13a47aa Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 7 Aug 2026 17:31:48 -0700 Subject: [PATCH 10/10] ci(workflows): pin Trivy cache key directly to action version instead of hashing workflow file --- .github/workflows/reusable-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-analysis.yml b/.github/workflows/reusable-analysis.yml index 4a0ff80c8..04d6e12ef 100644 --- a/.github/workflows/reusable-analysis.yml +++ b/.github/workflows/reusable-analysis.yml @@ -88,7 +88,7 @@ jobs: uses: actions/cache@v6 with: path: .trivycache - key: ${{ runner.os }}-trivy-${{ hashFiles('.github/workflows/reusable-analysis.yml') }} + key: ${{ runner.os }}-trivy-v0.36.0 restore-keys: | ${{ runner.os }}-trivy-