diff --git a/.github/workflows/container-scan.yml b/.github/workflows/container-scan.yml index 12275735..c1341c18 100644 --- a/.github/workflows/container-scan.yml +++ b/.github/workflows/container-scan.yml @@ -18,8 +18,8 @@ jobs: CONTAINER_SCAN_MERGED_SARIF_OUTPUT: container-scan-platform-backend-merged.sarif # SCA / CVE - TRIVY_IGNOREFILE: .github/scripts/suppress_trivy.yaml - OSV_IGNOREFILE: .github/scripts/suppress_osv_scanner.toml + TRIVY_IGNOREFILE: ci/suppress_trivy.yaml + OSV_IGNOREFILE: ci/suppress_osv_scanner.toml TRIVY_SCA_SARIF_OUTPUT: sca-trivy-container.sarif OSV_SCA_SARIF_OUTPUT: sca-osv-container.sarif @@ -44,15 +44,15 @@ jobs: - name: Setup tools run: | - bash .github/scripts/setup-tools.sh \ + bash ci/setup-tools.sh \ --install-tool trivy,osv-scanner,opengrep,hadolint,semgrep-rules - name: Run SAST scanning - run: python .github/scripts/container_scan.py --scan-type sast + run: python ci/container_scan.py --scan-type sast - name: Run SCA scanning if: always() - run: python .github/scripts/container_scan.py --scan-type sca --image ${{ env.IMAGE_NAME }} + run: python ci/container_scan.py --scan-type sca --image ${{ env.IMAGE_NAME }} - name: Upload Trivy SARIF to GitHub Security tab id: upload_trivy @@ -89,7 +89,7 @@ jobs: - name: Merge all SARIF reports if: always() run: | - python .github/scripts/container_scan.py \ + python ci/container_scan.py \ --merge-sarif "${{ env.TRIVY_SCA_SARIF_OUTPUT }}" "${{ env.OSV_SCA_SARIF_OUTPUT }}" "${{ env.OPENGREP_SAST_SARIF_OUTPUT }}" "${{ env.HADOLINT_SAST_SARIF_OUTPUT }}" \ --merge-output "${{ env.CONTAINER_SCAN_MERGED_SARIF_OUTPUT }}" diff --git a/.github/workflows/sast.yml b/.github/workflows/sast.yml index 1e65d042..786dbc81 100644 --- a/.github/workflows/sast.yml +++ b/.github/workflows/sast.yml @@ -18,7 +18,7 @@ jobs: semgrep-rules/generic semgrep-rules/problem-based-packs semgrep-rules/bash semgrep-rules/java auto semgrep-rules/yaml semgrep-rules/package_managers p/default OPENGREP_EXCLUDE: >- - *.sarif .github/scripts Dockerfile* .pre-commit-config.yaml docs/** README.md AGENTS.md + *.sarif ci/ Dockerfile* .pre-commit-config.yaml docs/** README.md AGENTS.md OPENGREP_SARIF_OUTPUT: sast-semgrep-app.sarif steps: @@ -32,10 +32,10 @@ jobs: python-version: '3.14.4' - name: Setup tools - run: bash .github/scripts/setup-tools.sh --install-tool opengrep,semgrep-rules + run: bash ci/setup-tools.sh --install-tool opengrep,semgrep-rules - name: Run SAST scanning - run: python .github/scripts/sast_scan.py + run: python ci/sast_scan.py - name: Upload Semgrep SARIF to GitHub Security tab id: upload_semgrep diff --git a/.github/workflows/sca.yml b/.github/workflows/sca.yml index 08b72a2e..c57bf84a 100644 --- a/.github/workflows/sca.yml +++ b/.github/workflows/sca.yml @@ -15,8 +15,8 @@ jobs: security-events: write # required for uploading SCA results to github security env: SBOM_PATH: target/bom.json - TRIVY_IGNOREFILE: .github/scripts/suppress_trivy.yaml - OSV_IGNOREFILE: .github/scripts/suppress_osv_scanner.toml + TRIVY_IGNOREFILE: ci/suppress_trivy.yaml + OSV_IGNOREFILE: ci/suppress_osv_scanner.toml TRIVY_SARIF_OUTPUT: trivy-platform-backend.sarif OSV_SARIF_OUTPUT: osv-scanner-platform-backend.sarif SCA_MERGED_SARIF_OUTPUT: SCA-platform-backend-merged.sarif @@ -42,10 +42,10 @@ jobs: run: mvn dependency:resolve -q - name: Setup tools - run: bash .github/scripts/setup-tools.sh --install-tool trivy,osv-scanner --sbom-ecosystem maven + run: bash ci/setup-tools.sh --install-tool trivy,osv-scanner --sbom-ecosystem maven - name: Run SCA tools - run: python .github/scripts/sca_scan.py + run: python ci/sca_scan.py - name: Upload Trivy SARIF to GitHub Security tab id: upload_trivy diff --git a/.github/README.md b/ci/README.md similarity index 92% rename from .github/README.md rename to ci/README.md index 14343aab..b3e80e4f 100644 --- a/.github/README.md +++ b/ci/README.md @@ -57,7 +57,7 @@ All three trigger independently and run in parallel; each uploads its own SARIF ## 3. Tool installation (`setup-tools.sh`) ```bash -bash .github/scripts/setup-tools.sh --install-tool [--sbom-ecosystem maven|npm|none] +bash ci/setup-tools.sh --install-tool [--sbom-ecosystem maven|npm|none] ``` `--install-tool` accepts a comma-separated list (or `all`): @@ -88,7 +88,7 @@ Both steps run regardless of each other (`if: always()`), all four SARIF files a `container_scan.py` is a single CLI shared by both scan types: ``` -$ python3 .github/scripts/container_scan.py --help +$ python3 ci/container_scan.py --help usage: sec-orchestrator [-h] [-s {sast,sca}] [-i IMAGE] [--merge-sarif SARIF_FILE [SARIF_FILE ...]] [--merge-output MERGE_OUTPUT] Agnostic DevSecOps Container scanning Pipeline Orchestrator @@ -107,9 +107,9 @@ options: **Running it locally:** ```bash docker build -t app:local . -bash .github/scripts/setup-tools.sh --install-tool trivy,osv-scanner,opengrep,hadolint,semgrep-rules -python .github/scripts/container_scan.py --scan-type sast -python .github/scripts/container_scan.py --scan-type sca --image app:local +bash ci/setup-tools.sh --install-tool trivy,osv-scanner,opengrep,hadolint,semgrep-rules +python ci/container_scan.py --scan-type sast +python ci/container_scan.py --scan-type sca --image app:local ``` ## 5. Pipeline: Software Composition Analysis (SCA) @@ -121,8 +121,8 @@ Both tools need to be installed first, same as Container Scanning, via `setup-to **Running it locally:** ```bash mvn dependency:resolve -q -bash .github/scripts/setup-tools.sh --install-tool trivy,osv-scanner --sbom-ecosystem maven # -> mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom -> target/bom.json -python .github/scripts/sca_scan.py +bash ci/setup-tools.sh --install-tool trivy,osv-scanner --sbom-ecosystem maven # -> mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom -> target/bom.json +python ci/sca_scan.py ``` `mvn dependency:resolve` pulls the full dependency tree into `.m2` first, so the CycloneDX plugin has something resolved to build the SBOM from. @@ -137,8 +137,8 @@ Trivy and OSV-Scanner both run against the SBOM, findings are evaluated by `pars **Running it locally:** ```bash -bash .github/scripts/setup-tools.sh --install-tool opengrep,semgrep-rules -python .github/scripts/sast_scan.py +bash ci/setup-tools.sh --install-tool opengrep,semgrep-rules +python ci/sast_scan.py ``` --- diff --git a/.github/scripts/container_scan.py b/ci/container_scan.py similarity index 97% rename from .github/scripts/container_scan.py rename to ci/container_scan.py index 018c1d35..e909dc01 100644 --- a/.github/scripts/container_scan.py +++ b/ci/container_scan.py @@ -23,8 +23,8 @@ IMAGE_NAME = os.getenv("IMAGE_NAME", "platform-backend:local") # --- SCA / CVE (Trivy + OSV) --- -TRIVY_IGNOREFILE = os.getenv("TRIVY_IGNOREFILE", ".github/scripts/suppress_trivy.yaml") -OSV_IGNOREFILE = os.getenv("OSV_IGNOREFILE", ".github/scripts/suppress_osv_scanner.toml") +TRIVY_IGNOREFILE = os.getenv("TRIVY_IGNOREFILE", "ci/suppress_trivy.yaml") +OSV_IGNOREFILE = os.getenv("OSV_IGNOREFILE", "ci/suppress_osv_scanner.toml") TRIVY_SCA_SARIF_OUTPUT = os.getenv("TRIVY_SCA_SARIF_OUTPUT", "sca-trivy-container.sarif") OSV_SCA_SARIF_OUTPUT = os.getenv("OSV_SCA_SARIF_OUTPUT", "sca-osv-container.sarif") diff --git a/.github/scripts/parse_sarif.py b/ci/parse_sarif.py similarity index 99% rename from .github/scripts/parse_sarif.py rename to ci/parse_sarif.py index f07d0249..5d35c970 100644 --- a/.github/scripts/parse_sarif.py +++ b/ci/parse_sarif.py @@ -36,4 +36,4 @@ def evaluate(sarif_paths): return EvaluationResult( gate_failed=max_score >= 8, gate_warn=5 <= max_score < 8, - ) \ No newline at end of file + ) diff --git a/.github/scripts/sast_scan.py b/ci/sast_scan.py similarity index 96% rename from .github/scripts/sast_scan.py rename to ci/sast_scan.py index 8213defb..5c40496d 100644 --- a/.github/scripts/sast_scan.py +++ b/ci/sast_scan.py @@ -23,7 +23,7 @@ ).split() OPENGREP_EXCLUDE = os.getenv( "OPENGREP_EXCLUDE", - "*.sarif .github/scripts Dockerfile* .pre-commit-config.yaml docs/** README.md AGENTS.md" + "*.sarif ci/ Dockerfile* .pre-commit-config.yaml docs/** README.md AGENTS.md" ).split() OPENGREP_SARIF_OUTPUT = os.getenv("OPENGREP_SARIF_OUTPUT", "sast-opengrep-app.sarif") diff --git a/.github/scripts/sca_scan.py b/ci/sca_scan.py similarity index 94% rename from .github/scripts/sca_scan.py rename to ci/sca_scan.py index 62441785..b3297d94 100644 --- a/.github/scripts/sca_scan.py +++ b/ci/sca_scan.py @@ -19,8 +19,8 @@ # Configurable values SBOM_PATH = os.getenv("SBOM_PATH", "target/bom.json") -TRIVY_IGNOREFILE = os.getenv("TRIVY_IGNOREFILE", ".github/scripts/suppress_trivy.yaml") -OSV_IGNOREFILE = os.getenv("OSV_IGNOREFILE", ".github/scripts/suppress_osv_scanner.toml") +TRIVY_IGNOREFILE = os.getenv("TRIVY_IGNOREFILE", "ci/suppress_trivy.yaml") +OSV_IGNOREFILE = os.getenv("OSV_IGNOREFILE", "ci/suppress_osv_scanner.toml") TRIVY_SARIF_OUTPUT = os.getenv("TRIVY_SARIF_OUTPUT", "trivy-platform-backend.sarif") OSV_SARIF_OUTPUT = os.getenv("OSV_SARIF_OUTPUT", "osv-scanner-platform-backend.sarif") SCA_MERGED_SARIF_OUTPUT = os.getenv("SCA_MERGED_SARIF_OUTPUT", "SCA-platform-backend-merged.sarif") @@ -36,7 +36,7 @@ def run_trivy(): def run_osv_scanner(): cmd = [ - "osv-scanner", "scan", "source", + "osv-scanner", "scan", "source", "--lockfile", SBOM_PATH, "--config", OSV_IGNOREFILE, "--format", "sarif", @@ -73,33 +73,33 @@ def main(): sarif_files = {"trivy": TRIVY_SARIF_OUTPUT, "osv-scanner": OSV_SARIF_OUTPUT} tool_status = {} # "PASSED" | "WARNING" | "FAILED" | "ERROR" gate_failed = False - + # Run each SCA tool and collect their exit codes for name, tool_fn in tools.items(): exit_code = tool_fn() logger.info("-" * 40) - + path = sarif_files[name] if exit_code != 0 and os.path.exists(path): logger.error(f"{RED}[!] {name} exit code {exit_code} but wrote {path}{RESET}") tool_status[name] = "ERROR" gate_failed = True - + merge_sarifs() # combined artifact only, not used for the gate decision # Evaluate each SARIF file for gate decision for name, path in sarif_files.items(): if name in tool_status: continue # already flagged ERROR above, don't overwrite it - + if not os.path.exists(path): logger.error(f"{RED}[!] {name} SARIF missing: {path},tool failed to run (not a vulnerability){RESET}") tool_status[name] = "ERROR" 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 @@ -107,7 +107,7 @@ def main(): tool_status[name] = "WARNING" # this tool found 5.0 <= CVSS < 8.0 else: tool_status[name] = "PASSED" # this tool found nothing >= 5.0 - + # Print summary of results logger.info(f"\n{BOLD}========== SCA PIPELINE SUMMARY =========={RESET}") for name, status in tool_status.items(): @@ -120,11 +120,11 @@ def main(): else: logger.error(f"[{name}]: {RED}FAILED (CVSS >= 8.0 found){RESET}") logger.info(f"{BOLD}=========================================={RESET}\n") - + # Exit with non-zero code if any tool failed the gate 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 + main() diff --git a/.github/scripts/setup-tools.sh b/ci/setup-tools.sh similarity index 100% rename from .github/scripts/setup-tools.sh rename to ci/setup-tools.sh diff --git a/.github/scripts/suppress_osv_scanner.toml b/ci/suppress_osv_scanner.toml similarity index 53% rename from .github/scripts/suppress_osv_scanner.toml rename to ci/suppress_osv_scanner.toml index b5075b31..0dc9630f 100644 --- a/.github/scripts/suppress_osv_scanner.toml +++ b/ci/suppress_osv_scanner.toml @@ -1,4 +1,4 @@ [[IgnoredVulns]] id = "GHSA-5jmj-h7xm-6q6v" ignoreUntil = 2026-09-30 -reason = "The proposed 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" diff --git a/.github/scripts/suppress_trivy.yaml b/ci/suppress_trivy.yaml similarity index 98% rename from .github/scripts/suppress_trivy.yaml rename to ci/suppress_trivy.yaml index 5c8cae89..f1ee9450 100644 --- a/.github/scripts/suppress_trivy.yaml +++ b/ci/suppress_trivy.yaml @@ -1,3 +1,3 @@ vulnerabilities: - id: CVE-2026-54515 - statement: "The proposed fix version 2.21.5 not yet released" \ No newline at end of file + statement: "The proposed fix version 2.21.5 not yet released"