From f68767747bf4fcf0c11ad4f4d6255fb1127ed8a1 Mon Sep 17 00:00:00 2001 From: moghit-eou Date: Tue, 16 Jun 2026 14:21:23 +0100 Subject: [PATCH 01/11] CI: implement native Trivy SCA pipeline --- .github/scripts/readme.md | 1 + .github/scripts/run_sca.py | 51 ++++++++++++++++++++++++++++++++++ .github/scripts/setup-tool.sh | 5 ++++ .github/scripts/setup-tools.sh | 5 ++++ .github/workflows/sca.yml | 37 ++++++++++++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 .github/scripts/readme.md create mode 100644 .github/scripts/run_sca.py create mode 100644 .github/scripts/setup-tool.sh create mode 100644 .github/scripts/setup-tools.sh create mode 100644 .github/workflows/sca.yml diff --git a/.github/scripts/readme.md b/.github/scripts/readme.md new file mode 100644 index 00000000..4c12b509 --- /dev/null +++ b/.github/scripts/readme.md @@ -0,0 +1 @@ +- [ ] TODO \ No newline at end of file diff --git a/.github/scripts/run_sca.py b/.github/scripts/run_sca.py new file mode 100644 index 00000000..98093da4 --- /dev/null +++ b/.github/scripts/run_sca.py @@ -0,0 +1,51 @@ +import subprocess +import os +import sys + + +# TODO: Add structured logging + + + +def run_trivy(): + cmd = [ + "trivy", "fs", ".", + "--format", "sarif", + "--output", "trivy.sarif", + "--exit-code", "1", + "--severity", "CRITICAL,HIGH"] + + result = subprocess.run(cmd) + + # TODO: Handle the result of the Trivy scan, check for vulnerabilities. + + return result.returncode + +def run_osv_scanner(): + # TODO: Implement OSV scanning logic here + pass + +def run_dependency_check(): + # TODO : Implement Dependency Check scanning logic here + pass + +def main(): + + tools = [run_trivy, run_osv_scanner, run_dependency_check] + + failed_ci = False + + for tool in tools: + code = tool() + + if code and code != 0: + failed_ci = True + + if failed_ci: + sys.exit(1) + else: + sys.exit(0) + +if __name__ == "__main__": + main() + \ No newline at end of file diff --git a/.github/scripts/setup-tool.sh b/.github/scripts/setup-tool.sh new file mode 100644 index 00000000..0aab3d30 --- /dev/null +++ b/.github/scripts/setup-tool.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e + +# Install Trivy +curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.71.1 \ No newline at end of file diff --git a/.github/scripts/setup-tools.sh b/.github/scripts/setup-tools.sh new file mode 100644 index 00000000..0aab3d30 --- /dev/null +++ b/.github/scripts/setup-tools.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e + +# Install Trivy +curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.71.1 \ No newline at end of file diff --git a/.github/workflows/sca.yml b/.github/workflows/sca.yml new file mode 100644 index 00000000..795ee958 --- /dev/null +++ b/.github/workflows/sca.yml @@ -0,0 +1,37 @@ +name: SCA CI + +on: + push: # To be removed after testing, SCA trigger on PRs only + branches: + - ci/sca-pipeline + pull_request: + + +jobs: + sca: + runs-on: ubuntu-latest + + permissions: + contents: read + security-events: write # required for uploading SCA results to github security + + steps: + - name: Check out repository + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 #v6.2.0 + with: + python-version: '3.14.4' + + - name: Setup tools + run: bash .github/scripts/setup-tools.sh + + - name: Run SCA tools + run: python .github/scripts/run_sca.py + + - name: Upload Trivy SARIF to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6 + with: + sarif_file: trivy.sarif \ No newline at end of file From 76b72e0f3a51eabdc8630f3dc0177ad5b33c5d5f Mon Sep 17 00:00:00 2001 From: moghit-eou Date: Wed, 17 Jun 2026 13:28:30 +0100 Subject: [PATCH 02/11] CI: migrate to Debian Trixie container and fix Trivy Maven rate limits Trivy fetches Maven dependencies remotely when the local cache is empty, which triggers a 429 rate limit from Maven Central and breaks the scan. Pre-resolving with `mvn dependency:resolve` fills the local cache first, so Trivy can scan without making external requests. ci: migrate to Debian Trixie container --- .github/scripts/setup-tool.sh | 5 ----- .github/workflows/sca.yml | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 .github/scripts/setup-tool.sh diff --git a/.github/scripts/setup-tool.sh b/.github/scripts/setup-tool.sh deleted file mode 100644 index 0aab3d30..00000000 --- a/.github/scripts/setup-tool.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -e - -# Install Trivy -curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.71.1 \ No newline at end of file diff --git a/.github/workflows/sca.yml b/.github/workflows/sca.yml index 795ee958..a3249aa9 100644 --- a/.github/workflows/sca.yml +++ b/.github/workflows/sca.yml @@ -10,6 +10,8 @@ on: jobs: sca: runs-on: ubuntu-latest + container: + image: debian:trixie permissions: contents: read @@ -27,6 +29,9 @@ jobs: - name: Setup tools run: bash .github/scripts/setup-tools.sh + - name: Resolve Maven dependencies + run: mvn dependency:resolve -q + - name: Run SCA tools run: python .github/scripts/run_sca.py From 42980e790724354fa6cbd78a6eec05a863e16323 Mon Sep 17 00:00:00 2001 From: moghit-eou Date: Wed, 17 Jun 2026 14:26:07 +0100 Subject: [PATCH 03/11] ci: install missing dependencies in Debian Trixie container refactor: downrade python to 3.13 refactor(ci): move maven installation to setup script Maven is not installed in the Debian Trixie container by default, which is required for SCA dependency resolution. ci: cache Maven dependencies to fix Trivy 429 rate limit Trivy fetches Maven BOM files directly from Maven Central when the locals cache is empty, triggering a 429 rate limit block. Added Maven dependency caching (~/.m2) using actions/cache, keyed on pom.xml content hash. This ensures Maven Central is only hit when dependencies actually change, and Trivy scans from local cache. --- .github/scripts/run_sca.py | 3 ++- .github/scripts/setup-tools.sh | 4 ++++ .github/workflows/sca.yml | 8 +++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/scripts/run_sca.py b/.github/scripts/run_sca.py index 98093da4..9a880264 100644 --- a/.github/scripts/run_sca.py +++ b/.github/scripts/run_sca.py @@ -13,7 +13,8 @@ def run_trivy(): "--format", "sarif", "--output", "trivy.sarif", "--exit-code", "1", - "--severity", "CRITICAL,HIGH"] + "--severity", "CRITICAL,HIGH", + "--cache-dir", "/root/.m2"] result = subprocess.run(cmd) diff --git a/.github/scripts/setup-tools.sh b/.github/scripts/setup-tools.sh index 0aab3d30..c8bb7fc7 100644 --- a/.github/scripts/setup-tools.sh +++ b/.github/scripts/setup-tools.sh @@ -1,5 +1,9 @@ #!/bin/bash set -e +# Installing Maven for SCA dependency resolution +apt-get update -q +apt-get install -y curl ca-certificates maven + # Install Trivy curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.71.1 \ No newline at end of file diff --git a/.github/workflows/sca.yml b/.github/workflows/sca.yml index a3249aa9..0159f00c 100644 --- a/.github/workflows/sca.yml +++ b/.github/workflows/sca.yml @@ -24,11 +24,17 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 #v6.2.0 with: - python-version: '3.14.4' + python-version: '3.13' - name: Setup tools run: bash .github/scripts/setup-tools.sh + - name: Cache Maven dependencies + uses: actions/cache@v4 + with: + path: ~/.m2 + key: maven-${{ hashFiles('**/pom.xml') }} # generates a unique hash based on the content of pom.xml. + - name: Resolve Maven dependencies run: mvn dependency:resolve -q From 4a8e6bb81da170473425f9884a88e77c15cd2393 Mon Sep 17 00:00:00 2001 From: moghit-eou Date: Sun, 28 Jun 2026 14:55:18 +0100 Subject: [PATCH 04/11] feat: implement SBOM-based SCA pipeline with vulnerability patches - Switch all SCA tools (Trivy, OSV Scanner, Dependency-Check) to use CycloneDX SBOM - Patch jackson-bom versions in pom.xml - Add OSV Scanner suppression for GHSA-5jmj-h7xm-6q6v (fix not yet released) https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.21 --- .github/scripts/osv-scanner.toml | 4 +++ .github/scripts/run_sca.py | 58 +++++++++++++++++++++----------- .github/scripts/setup-tools.sh | 17 +++++++--- .github/workflows/sca.yml | 33 ++++++++++++------ pom.xml | 4 +++ 5 files changed, 81 insertions(+), 35 deletions(-) create mode 100644 .github/scripts/osv-scanner.toml diff --git a/.github/scripts/osv-scanner.toml b/.github/scripts/osv-scanner.toml new file mode 100644 index 00000000..9f4faeeb --- /dev/null +++ b/.github/scripts/osv-scanner.toml @@ -0,0 +1,4 @@ +[[IgnoredVulns]] +id = "GHSA-5jmj-h7xm-6q6v" +ignoreUntil = 2026-09-30 +reason = "Fix version 2.21.5 not yet released" \ No newline at end of file diff --git a/.github/scripts/run_sca.py b/.github/scripts/run_sca.py index 9a880264..861d185f 100644 --- a/.github/scripts/run_sca.py +++ b/.github/scripts/run_sca.py @@ -5,30 +5,40 @@ # TODO: Add structured logging - - def run_trivy(): cmd = [ - "trivy", "fs", ".", - "--format", "sarif", - "--output", "trivy.sarif", - "--exit-code", "1", + "trivy", "sbom", + "target/bom.json", + "--format", "sarif", + "--output", "trivy.sarif", "--severity", "CRITICAL,HIGH", - "--cache-dir", "/root/.m2"] + "--exit-code", "1" + ] - result = subprocess.run(cmd) - - # TODO: Handle the result of the Trivy scan, check for vulnerabilities. - - return result.returncode + return subprocess.run(cmd).returncode def run_osv_scanner(): - # TODO: Implement OSV scanning logic here - pass + cmd = [ + "osv-scanner", "scan", "source", + "--lockfile", "target/bom.json", + "--config", ".github/scripts/osv-scanner.toml", + "--format", "sarif", + "--output-file", "osv-scanner.sarif", + ] + + return subprocess.run(cmd).returncode def run_dependency_check(): - # TODO : Implement Dependency Check scanning logic here - pass + cmd = [ + "./dependency-check/bin/dependency-check.sh", + "--scan", "target/bom.json", + "--nvdDatafeed", "https://dependency-check.github.io/DependencyCheck_Builder/nvd_cache/", + "--format", "SARIF", + "--out", ".", + "--failOnCVSS", "8", + ] + + return subprocess.run(cmd).returncode def main(): @@ -36,16 +46,26 @@ def main(): failed_ci = False + results = {} + for tool in tools: code = tool() + results[tool.__name__] = code if code and code != 0: + print(f"Tool {tool.__name__} failed with exit code {code}") failed_ci = True - + print("\n\n----------------------------------\n\n") + + + print("\n\n========== SCA SUMMARY ==========") + for tool_name, code in results.items(): + status = "PASSED" if code == 0 else f"FAILED (exit code {code})" + print(f"{tool_name}: {status}") + print("==================================\n\n") + if failed_ci: sys.exit(1) - else: - sys.exit(0) if __name__ == "__main__": main() diff --git a/.github/scripts/setup-tools.sh b/.github/scripts/setup-tools.sh index c8bb7fc7..207c6810 100644 --- a/.github/scripts/setup-tools.sh +++ b/.github/scripts/setup-tools.sh @@ -1,9 +1,16 @@ #!/bin/bash set -e -# Installing Maven for SCA dependency resolution -apt-get update -q -apt-get install -y curl ca-certificates maven - # Install Trivy -curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.71.1 \ No newline at end of file +curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.71.1 + +# Install OSV Scanner +curl -L https://github.com/google/osv-scanner/releases/download/v2.4.0/osv-scanner_linux_amd64 -o /usr/local/bin/osv-scanner +chmod +x /usr/local/bin/osv-scanner + +# Generate the BOM using CycloneDX Maven Plugin +mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom -q + +# Install Dependency-Check +curl -L https://github.com/dependency-check/DependencyCheck/releases/download/v12.2.2/dependency-check-12.2.2-release.zip -o dependency-check.zip +unzip dependency-check.zip -d . \ No newline at end of file diff --git a/.github/workflows/sca.yml b/.github/workflows/sca.yml index 0159f00c..4df9096d 100644 --- a/.github/workflows/sca.yml +++ b/.github/workflows/sca.yml @@ -3,16 +3,13 @@ name: SCA CI on: push: # To be removed after testing, SCA trigger on PRs only branches: - - ci/sca-pipeline + - CI/sca-pipeline-sbom pull_request: jobs: sca: - runs-on: ubuntu-latest - container: - image: debian:trixie - + runs-on: ubuntu-latest # TODO: Migrate back to debian:trixie once pipeline MVP is fully stable permissions: contents: read security-events: write # required for uploading SCA results to github security @@ -24,17 +21,19 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 #v6.2.0 with: - python-version: '3.13' + python-version: '3.14.4' - name: Setup tools run: bash .github/scripts/setup-tools.sh - - name: Cache Maven dependencies + + - name: Cache Maven packages uses: actions/cache@v4 with: - path: ~/.m2 - key: maven-${{ hashFiles('**/pom.xml') }} # generates a unique hash based on the content of pom.xml. - + path: ~/.m2/repository # /.m2 only is too braod and can cause cache corruption issues + key: ${{ runner.os }}-m2-v1-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2-v1- + - name: Resolve Maven dependencies run: mvn dependency:resolve -q @@ -45,4 +44,16 @@ jobs: if: always() uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6 with: - sarif_file: trivy.sarif \ No newline at end of file + sarif_file: trivy.sarif + + - name: Upload Dependency Check SARIF to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6 + with: + sarif_file: dependency-check-report.sarif + + - name: Upload OSV Scanner SARIF to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6 + with: + sarif_file: osv-scanner.sarif \ No newline at end of file diff --git a/pom.xml b/pom.xml index b8a333a0..6be36b0f 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,10 @@ 11.0.22 2.6 10.9.1 + + 2.21.4 + 3.1.4 + From 8e943b654ac6dbf3698d3ee6b1fd935fa78cf751 Mon Sep 17 00:00:00 2001 From: moghit-eou Date: Sun, 28 Jun 2026 15:47:28 +0100 Subject: [PATCH 05/11] feat: add structured logging to SCA pipeline --- .github/scripts/run_sca.py | 27 +++++++++++++++++++-------- pom.xml | 2 -- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.github/scripts/run_sca.py b/.github/scripts/run_sca.py index 861d185f..8a920f52 100644 --- a/.github/scripts/run_sca.py +++ b/.github/scripts/run_sca.py @@ -1,9 +1,18 @@ import subprocess import os import sys +import logging +GREEN = '\033[92m' +RED = '\033[91m' +RESET = '\033[0m' +BOLD = '\033[1m' -# TODO: Add structured logging +logging.basicConfig( + level=logging.INFO, + format='%(message)s' +) +logger = logging.getLogger("sca-orchestrator") def run_trivy(): cmd = [ @@ -53,18 +62,20 @@ def main(): results[tool.__name__] = code if code and code != 0: - print(f"Tool {tool.__name__} failed with exit code {code}") + logger.error(f"{RED}[!] Tool {tool.__name__} failed with exit code {code}{RESET}") failed_ci = True - print("\n\n----------------------------------\n\n") + logger.info("-" * 40) - - print("\n\n========== SCA SUMMARY ==========") + logger.info(f"\n{BOLD}========== SCA PIPELINE SUMMARY =========={RESET}") for tool_name, code in results.items(): - status = "PASSED" if code == 0 else f"FAILED (exit code {code})" - print(f"{tool_name}: {status}") - print("==================================\n\n") + if code == 0: + logger.info(f"[{tool_name}]: {GREEN}PASSED{RESET}") + else: + logger.error(f"[{tool_name}]: {RED}FAILED (Exit Code {code}){RESET}") + logger.info(f"{BOLD}=========================================={RESET}\n") if failed_ci: + logger.error(f"{RED}Pipeline blocked due to security findings.{RESET}") sys.exit(1) if __name__ == "__main__": diff --git a/pom.xml b/pom.xml index 6be36b0f..7d5b41a8 100644 --- a/pom.xml +++ b/pom.xml @@ -30,10 +30,8 @@ 11.0.22 2.6 10.9.1 - 2.21.4 3.1.4 - From 1271fde3ab2699fd58c9d6167f99dddaf0cb78eb Mon Sep 17 00:00:00 2001 From: moghit-eou Date: Sun, 28 Jun 2026 15:55:12 +0100 Subject: [PATCH 06/11] chore: pin actions/cache to commt SHA --- .github/workflows/sca.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/sca.yml b/.github/workflows/sca.yml index 4df9096d..0e84ff79 100644 --- a/.github/workflows/sca.yml +++ b/.github/workflows/sca.yml @@ -1,9 +1,6 @@ name: SCA CI on: - push: # To be removed after testing, SCA trigger on PRs only - branches: - - CI/sca-pipeline-sbom pull_request: @@ -28,7 +25,7 @@ jobs: - name: Cache Maven packages - uses: actions/cache@v4 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 #v6.1.0 with: path: ~/.m2/repository # /.m2 only is too braod and can cause cache corruption issues key: ${{ runner.os }}-m2-v1-${{ hashFiles('**/pom.xml') }} From 44c694c542b1bbf19f1805fda76fffdf50b7d519 Mon Sep 17 00:00:00 2001 From: moghit-eou Date: Sun, 28 Jun 2026 15:58:29 +0100 Subject: [PATCH 07/11] test: negative testing intentionally unpin Jackson dependencies to verify CI blocks vulnerabilities --- pom.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7d5b41a8..b8a333a0 100644 --- a/pom.xml +++ b/pom.xml @@ -30,8 +30,6 @@ 11.0.22 2.6 10.9.1 - 2.21.4 - 3.1.4 From 6a570905d75089e97d635d33c1835996e83ac073 Mon Sep 17 00:00:00 2001 From: moghit-eou Date: Sun, 28 Jun 2026 16:07:33 +0100 Subject: [PATCH 08/11] docs: add readme --- .github/scripts/readme.md | 11 ++++++++++- pom.xml | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/scripts/readme.md b/.github/scripts/readme.md index 4c12b509..2061ca7a 100644 --- a/.github/scripts/readme.md +++ b/.github/scripts/readme.md @@ -1 +1,10 @@ -- [ ] TODO \ No newline at end of file +# SCA Pipeline + +Security scanning runs automatically on every pull request to detect vulnerable dependencies before merging. +We generate a CycloneDX SBOM from the project to ensure accurate results with no false positives. + +## Files + +- **setup-tools.sh** : Installs the scanning tools: Trivy, OSV Scanner, and OWASP Dependency-Check. +- **run_sca.py** : Orchestrates all three tools and prints a pass/fail summary at the end. +- **osv-scanner.toml** : OSV Scanner suppression list for vulnerabilities that cannot be fixed yet, with reasons and expiry dates. \ No newline at end of file diff --git a/pom.xml b/pom.xml index b8a333a0..7d5b41a8 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,8 @@ 11.0.22 2.6 10.9.1 + 2.21.4 + 3.1.4 From 2186711769f0e1eadc25dcb4d7209599f5fd017e Mon Sep 17 00:00:00 2001 From: moghit-eou Date: Sun, 28 Jun 2026 16:24:34 +0100 Subject: [PATCH 09/11] ci: add display name --- .github/workflows/sca.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/sca.yml b/.github/workflows/sca.yml index 0e84ff79..165bb5be 100644 --- a/.github/workflows/sca.yml +++ b/.github/workflows/sca.yml @@ -6,6 +6,7 @@ on: jobs: sca: + name: Software Composition Analysis runs-on: ubuntu-latest # TODO: Migrate back to debian:trixie once pipeline MVP is fully stable permissions: contents: read From fc4877785bb0f6145f4c9d4e814fff104ccdbcae Mon Sep 17 00:00:00 2001 From: moghit-eou Date: Sat, 4 Jul 2026 18:20:26 +0100 Subject: [PATCH 10/11] feat(sca): add container image scanning SCA pipeline --- .github/scripts/parse_sarif.py | 30 +++++ .github/scripts/run_sca.py | 83 ------------- .github/scripts/run_sca_app.py | 110 ++++++++++++++++++ .github/scripts/run_sca_image.py | 109 +++++++++++++++++ .github/scripts/setup-tools.sh | 25 +++- ...-scanner.toml => supress_osv_scanner.toml} | 2 +- .github/scripts/supress_trivy.yaml | 3 + .github/workflows/{sca.yml => sca_app.yml} | 33 +++--- .github/workflows/sca_image.yml | 51 ++++++++ Dockerfile | 2 +- 10 files changed, 342 insertions(+), 106 deletions(-) create mode 100644 .github/scripts/parse_sarif.py delete mode 100644 .github/scripts/run_sca.py create mode 100644 .github/scripts/run_sca_app.py create mode 100644 .github/scripts/run_sca_image.py rename .github/scripts/{osv-scanner.toml => supress_osv_scanner.toml} (53%) create mode 100644 .github/scripts/supress_trivy.yaml rename .github/workflows/{sca.yml => sca_app.yml} (75%) create mode 100644 .github/workflows/sca_image.yml diff --git a/.github/scripts/parse_sarif.py b/.github/scripts/parse_sarif.py new file mode 100644 index 00000000..16af3b4a --- /dev/null +++ b/.github/scripts/parse_sarif.py @@ -0,0 +1,30 @@ +import json +from dataclasses import dataclass + +@dataclass +class EvaluationResult: + gate_failed: bool + gate_warn: bool + +def evaluate(sarif_paths): + if isinstance(sarif_paths, str): + sarif_paths = [sarif_paths] + + max_score = 0.0 + for path in sarif_paths: + with open(path) as f: + sarif = json.load(f, strict=False) + + for run in sarif.get("runs", []): + rules = run["tool"]["driver"].get("rules", []) + severities = {r["id"]: r.get("properties", {}).get("security-severity") for r in rules} + + for result in run.get("results", []): + score = severities.get(result["ruleId"]) + if score is not None: + max_score = max(max_score, float(score)) + + return EvaluationResult( + gate_failed=max_score >= 8, + gate_warn=5 <= max_score < 8, + ) \ No newline at end of file diff --git a/.github/scripts/run_sca.py b/.github/scripts/run_sca.py deleted file mode 100644 index 8a920f52..00000000 --- a/.github/scripts/run_sca.py +++ /dev/null @@ -1,83 +0,0 @@ -import subprocess -import os -import sys -import logging - -GREEN = '\033[92m' -RED = '\033[91m' -RESET = '\033[0m' -BOLD = '\033[1m' - -logging.basicConfig( - level=logging.INFO, - format='%(message)s' -) -logger = logging.getLogger("sca-orchestrator") - -def run_trivy(): - cmd = [ - "trivy", "sbom", - "target/bom.json", - "--format", "sarif", - "--output", "trivy.sarif", - "--severity", "CRITICAL,HIGH", - "--exit-code", "1" - ] - - return subprocess.run(cmd).returncode - -def run_osv_scanner(): - cmd = [ - "osv-scanner", "scan", "source", - "--lockfile", "target/bom.json", - "--config", ".github/scripts/osv-scanner.toml", - "--format", "sarif", - "--output-file", "osv-scanner.sarif", - ] - - return subprocess.run(cmd).returncode - -def run_dependency_check(): - cmd = [ - "./dependency-check/bin/dependency-check.sh", - "--scan", "target/bom.json", - "--nvdDatafeed", "https://dependency-check.github.io/DependencyCheck_Builder/nvd_cache/", - "--format", "SARIF", - "--out", ".", - "--failOnCVSS", "8", - ] - - return subprocess.run(cmd).returncode - -def main(): - - tools = [run_trivy, run_osv_scanner, run_dependency_check] - - failed_ci = False - - results = {} - - for tool in tools: - code = tool() - results[tool.__name__] = code - - if code and code != 0: - logger.error(f"{RED}[!] Tool {tool.__name__} failed with exit code {code}{RESET}") - failed_ci = True - logger.info("-" * 40) - - logger.info(f"\n{BOLD}========== SCA PIPELINE SUMMARY =========={RESET}") - for tool_name, code in results.items(): - if code == 0: - logger.info(f"[{tool_name}]: {GREEN}PASSED{RESET}") - else: - logger.error(f"[{tool_name}]: {RED}FAILED (Exit Code {code}){RESET}") - logger.info(f"{BOLD}=========================================={RESET}\n") - - if failed_ci: - logger.error(f"{RED}Pipeline blocked due to security findings.{RESET}") - sys.exit(1) - -if __name__ == "__main__": - main() - \ No newline at end of file diff --git a/.github/scripts/run_sca_app.py b/.github/scripts/run_sca_app.py new file mode 100644 index 00000000..455e652c --- /dev/null +++ b/.github/scripts/run_sca_app.py @@ -0,0 +1,110 @@ +import subprocess +import os +import sys +import logging +import json +from parse_sarif import evaluate + +GREEN = '\033[92m' +RED = '\033[91m' +RESET = '\033[0m' +BOLD = '\033[1m' +YELLOW = '\033[93m' + +logging.basicConfig( + level=logging.INFO, + format='%(message)s' # Clean format to prevent double-timestamps in CI logs +) +logger = logging.getLogger("sca-orchestrator") + +def run_trivy(): + cmd = [ + "trivy", "sbom", + "target/bom.json", + "--format", "sarif", + "--ignorefile", ".github/scripts/supress_trivy.yaml", + "--output", "trivy-app.sarif" + ] + + return subprocess.run(cmd).returncode + +def run_osv_scanner(): + cmd = [ + "osv-scanner", "scan", "source", + "--lockfile", "target/bom.json", + "--config", ".github/scripts/supress_osv_scanner.toml", + "--format", "sarif", + "--output-file", "osv-scanner-app.sarif" + ] + + return subprocess.run(cmd).returncode + +def merge_sarifs(): + merged = { + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [], + } + + for path in ("trivy-app.sarif", "osv-scanner-app.sarif"): + if not os.path.exists(path): + logger.warning(f"{path} not found, skipping in merge") + continue + with open(path) as f: + sarif = json.load(f, strict=False) + merged["runs"].extend(sarif.get("runs", [])) + + with open("merged-SCA-platform-backend-app.sarif", "w") as f: + json.dump(merged, f) + + logger.info("SARIF files merged successfully.") + + + +def main(): + tools = [run_trivy, run_osv_scanner] + + exit_codes = {} + for tool in tools: + exit_codes[tool.__name__] = tool() + logger.info("-" * 40) + + merge_sarifs() # combined artifact only, not used for the gate decision + + sarif_files = {"trivy": "trivy-app.sarif", "osv-scanner": "osv-scanner-app.sarif"} + tool_status = {} # "PASSED" | "WARNING" | "FAILED" + gate_failed = False + + for name, path in sarif_files.items(): + if not os.path.exists(path): + logger.error(f"{RED}[!] {name} SARIF file missing, skipping evaluation: {path}{RESET}") + tool_status[name] = "FAILED, Something is wrong with the tool execution, please check the logs." + gate_failed = True + continue + + eval_result = evaluate(path) + + if eval_result.gate_failed: + tool_status[name] = "FAILED" # this tool found CVSS >= 8.0 + gate_failed = True + elif eval_result.gate_warn: + tool_status[name] = "WARNING" # this tool found 5.0 <= CVSS < 8.0 + else: + tool_status[name] = "PASSED" # this tool found nothing >= 5.0 + + logger.info(f"\n{BOLD}========== SCA PIPELINE SUMMARY =========={RESET}") + for name, status in tool_status.items(): + if status == "PASSED": + logger.info(f"[{name}]: {GREEN}PASSED{RESET}") + elif status == "WARNING": + logger.warning(f"[{name}]: {YELLOW}WARNING (findings between 5.0 and 8.0){RESET}") + else: + logger.error(f"[{name}]: {RED}FAILED (CVSS >= 8.0 found){RESET}") + logger.info(f"{BOLD}=========================================={RESET}\n") + + if gate_failed: + logger.error(f"{RED}One or more SCA tools failed the gate check.{RESET}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/.github/scripts/run_sca_image.py b/.github/scripts/run_sca_image.py new file mode 100644 index 00000000..a20a8457 --- /dev/null +++ b/.github/scripts/run_sca_image.py @@ -0,0 +1,109 @@ +import subprocess +import os +import sys +import logging +import json +from parse_sarif import evaluate + +GREEN = '\033[92m' +RED = '\033[91m' +RESET = '\033[0m' +BOLD = '\033[1m' +YELLOW = '\033[93m' + +logging.basicConfig( + level=logging.INFO, + format='%(message)s' # Clean format to prevent double-timestamps in CI logs +) +logger = logging.getLogger("sca-orchestrator") +def run_trivy(): + cmd = [ + "trivy", "image", + "platform-backend:testing", + "--format", "sarif", + "--ignorefile", ".github/scripts/supress_trivy.yaml", + "--output", "trivy-image.sarif" + ] + return subprocess.run(cmd).returncode + + +def run_osv_scanner(): + cmd = [ + "osv-scanner", "scan", "image", + "platform-backend:testing", + "--config", ".github/scripts/supress_osv_scanner.toml", + "--format", "sarif", + "--output-file", "osv-scanner-image.sarif" + ] + return subprocess.run(cmd).returncode + +def merge_sarifs(): + + merged = { + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [], + } + + for path in ("trivy-image.sarif", "osv-scanner-image.sarif"): + if not os.path.exists(path): + logger.warning(f"{path} not found, skipping in merge") + continue + with open(path) as f: + sarif = json.load(f, strict=False) + merged["runs"].extend(sarif.get("runs", [])) + + with open("merged-SCA-platform-backend-image.sarif", "w") as f: + json.dump(merged, f) + + logger.info("SARIF files merged successfully.") + + + +def main(): + tools = [run_trivy, run_osv_scanner] + + exit_codes = {} + for tool in tools: + exit_codes[tool.__name__] = tool() + logger.info("-" * 40) + + merge_sarifs() # combined artifact only, not used for the gate decision + + sarif_files = {"trivy": "trivy-image.sarif", "osv-scanner": "osv-scanner-image.sarif"} + tool_status = {} # "PASSED" | "WARNING" | "FAILED" + gate_failed = False + + for name, path in sarif_files.items(): + if not os.path.exists(path): + logger.error(f"{RED}[!] {name} SARIF file missing, skipping evaluation: {path}{RESET}") + tool_status[name] = "FAILED, Something is wrong with the tool execution, please check the logs." + gate_failed = True + continue + + eval_result = evaluate(path) + + if eval_result.gate_failed: + tool_status[name] = "FAILED" # this tool found CVSS >= 8.0 + gate_failed = True # Fail the gate if any tool fails + elif eval_result.gate_warn: + tool_status[name] = "WARNING" # this tool found 5.0 <= CVSS < 8.0 + else: + tool_status[name] = "PASSED" # this tool found nothing >= 5.0 + + logger.info(f"\n{BOLD}========== SCA PIPELINE SUMMARY =========={RESET}") + for name, status in tool_status.items(): + if status == "PASSED": + logger.info(f"[{name}]: {GREEN}PASSED{RESET}") + elif status == "WARNING": + logger.warning(f"[{name}]: {YELLOW}WARNING (findings between 5.0 and 8.0){RESET}") + else: + logger.error(f"[{name}]: {RED}FAILED (CVSS >= 8.0 found){RESET}") + logger.info(f"{BOLD}=========================================={RESET}\n") + + if gate_failed: + logger.error(f"{RED}One or more SCA tools failed the gate check.{RESET}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/.github/scripts/setup-tools.sh b/.github/scripts/setup-tools.sh index 207c6810..d502f886 100644 --- a/.github/scripts/setup-tools.sh +++ b/.github/scripts/setup-tools.sh @@ -8,9 +8,24 @@ curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/inst curl -L https://github.com/google/osv-scanner/releases/download/v2.4.0/osv-scanner_linux_amd64 -o /usr/local/bin/osv-scanner chmod +x /usr/local/bin/osv-scanner -# Generate the BOM using CycloneDX Maven Plugin -mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom -q -# Install Dependency-Check -curl -L https://github.com/dependency-check/DependencyCheck/releases/download/v12.2.2/dependency-check-12.2.2-release.zip -o dependency-check.zip -unzip dependency-check.zip -d . \ No newline at end of file +# Generate SBOM based on project type + +PROJECT_TYPE="${1:-none}" # maven | npm | none +case "$PROJECT_TYPE" in + maven) + echo "Generating SBOM for Maven project" + mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom -q + ;; + npm) + echo "Generating SBOM for NPM project" + npx --yes @cyclonedx/cyclonedx-npm --output-file bom.json + ;; + none) + echo "No SBOM generation needed for image pipelines" + ;; + *) + echo "Unknown PROJECT_TYPE: $PROJECT_TYPE" >&2 # redirect error message to stderr + exit 1 + ;; +esac \ No newline at end of file diff --git a/.github/scripts/osv-scanner.toml b/.github/scripts/supress_osv_scanner.toml similarity index 53% rename from .github/scripts/osv-scanner.toml rename to .github/scripts/supress_osv_scanner.toml index 9f4faeeb..b5075b31 100644 --- a/.github/scripts/osv-scanner.toml +++ b/.github/scripts/supress_osv_scanner.toml @@ -1,4 +1,4 @@ [[IgnoredVulns]] id = "GHSA-5jmj-h7xm-6q6v" ignoreUntil = 2026-09-30 -reason = "Fix version 2.21.5 not yet released" \ No newline at end of file +reason = "The proposed fix version 2.21.5 not yet released" \ No newline at end of file diff --git a/.github/scripts/supress_trivy.yaml b/.github/scripts/supress_trivy.yaml new file mode 100644 index 00000000..5c8cae89 --- /dev/null +++ b/.github/scripts/supress_trivy.yaml @@ -0,0 +1,3 @@ +vulnerabilities: + - id: CVE-2026-54515 + statement: "The proposed fix version 2.21.5 not yet released" \ No newline at end of file diff --git a/.github/workflows/sca.yml b/.github/workflows/sca_app.yml similarity index 75% rename from .github/workflows/sca.yml rename to .github/workflows/sca_app.yml index 165bb5be..2b560ffd 100644 --- a/.github/workflows/sca.yml +++ b/.github/workflows/sca_app.yml @@ -1,9 +1,8 @@ -name: SCA CI +name: SCA CI - app on: pull_request: - jobs: sca: name: Software Composition Analysis @@ -20,38 +19,40 @@ jobs: uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 #v6.2.0 with: python-version: '3.14.4' - - - name: Setup tools - run: bash .github/scripts/setup-tools.sh - - + - name: Cache Maven packages uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 #v6.1.0 with: path: ~/.m2/repository # /.m2 only is too braod and can cause cache corruption issues key: ${{ runner.os }}-m2-v1-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2-v1- - + - name: Resolve Maven dependencies run: mvn dependency:resolve -q + - name: Setup tools + run: bash .github/scripts/setup-tools.sh maven + - name: Run SCA tools - run: python .github/scripts/run_sca.py + run: python .github/scripts/run_sca_app.py - name: Upload Trivy SARIF to GitHub Security tab + id: upload_trivy if: always() uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6 with: - sarif_file: trivy.sarif - - - name: Upload Dependency Check SARIF to GitHub Security tab + sarif_file: trivy-app.sarif + + - name: Upload OSV Scanner SARIF to GitHub Security tab + id: upload_osv if: always() uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6 with: - sarif_file: dependency-check-report.sarif + sarif_file: osv-scanner-app.sarif - - name: Upload OSV Scanner SARIF to GitHub Security tab - if: always() + - name: Upload merged SARIF to GitHub Security tab + if: ${{ always() && steps.upload_trivy.outcome == 'success' && steps.upload_osv.outcome == 'success' }} uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6 with: - sarif_file: osv-scanner.sarif \ No newline at end of file + sarif_file: merged-SCA-platform-backend-app.sarif + category: merged-sca \ No newline at end of file diff --git a/.github/workflows/sca_image.yml b/.github/workflows/sca_image.yml new file mode 100644 index 00000000..6b2e614a --- /dev/null +++ b/.github/workflows/sca_image.yml @@ -0,0 +1,51 @@ +name: SCA CI - image + +on: + pull_request: + +jobs: + sca: + name: Software Composition Analysis + runs-on: ubuntu-latest # TODO: Migrate back to debian:trixie once pipeline MVP is fully stable + permissions: + contents: read + security-events: write # required for uploading SCA results to github security + + steps: + - name: Check out repository + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 #v6.2.0 + with: + python-version: '3.14.4' + + - name: Build Docker image + run: docker build -t platform-backend:testing . + + - name: Setup tools + run: bash .github/scripts/setup-tools.sh + + - name: Run SCA tools + run: python .github/scripts/run_sca_image.py + + - name: Upload Trivy SARIF to GitHub Security tab + id: upload_trivy + if: always() + uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6 + with: + sarif_file: trivy-image.sarif + + - name: Upload OSV Scanner SARIF to GitHub Security tab + id: upload_osv + if: always() + uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6 + with: + sarif_file: osv-scanner-image.sarif + + - name: Upload merged SARIF to GitHub Security tab + if: ${{ always() && steps.upload_trivy.outcome == 'success' && steps.upload_osv.outcome == 'success' }} + uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6 + with: + sarif_file: merged-SCA-platform-backend-image.sarif + category: merged-sca \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index aa9e3710..fad0211f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,7 +46,7 @@ RUN apk add --no-cache curl ####################################################### # Install dockerize ####################################################### -ENV DOCKERIZE_VERSION=v0.10.1 +ENV DOCKERIZE_VERSION=v0.10.0 RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ && tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ && rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz From 3150cbf341b50e531953b354676a8aaa9b2cc35e Mon Sep 17 00:00:00 2001 From: moghit-eou Date: Sun, 5 Jul 2026 17:47:20 +0100 Subject: [PATCH 11/11] chore(docker): bump dockerize from v0.10.0 to v0.13.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index fad0211f..2fb4aa23 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,7 +46,7 @@ RUN apk add --no-cache curl ####################################################### # Install dockerize ####################################################### -ENV DOCKERIZE_VERSION=v0.10.0 +ENV DOCKERIZE_VERSION=v0.13.0 RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ && tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ && rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz