diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml new file mode 100644 index 00000000..908a07b0 --- /dev/null +++ b/.github/workflows/security-scan.yml @@ -0,0 +1,111 @@ +# Central bundled security gate for every ContextualWisdomLab repo. +# +# This is a REQUIRED org workflow (see the "CWL Central required workflows" +# ruleset). It bundles the supply-chain / vulnerability / posture scanners into +# one gate so they pass or fail as a unit: +# +# osv-scan HARD diff-scoped — fails on NEW vulns the PR introduces +# dependency-review HARD diff-scoped — fails on vulnerable/denied deps the PR adds +# trivy-fs HARD repo-wide — fails on FIXABLE CRITICAL/HIGH findings +# scorecard SOFT repo posture — uploaded for visibility, never blocks +# +# Gating is by the JOB result (a failed job fails this required workflow -> +# merge blocked), NOT by the code_scanning ruleset rule. The code_scanning rule +# stays CodeQL-only on purpose: requiring multiple code-scanning TOOLS there is +# unsatisfiable because default-setup CodeQL uploads to refs/pull/N/head while +# pull_request workflows upload to refs/pull/N/merge, so no single ref ever holds +# all tools. Bundling at the workflow/check level is ref-independent. +# +# NOTE on trivy-fs: it scans the whole repo, so a pre-existing FIXABLE +# CRITICAL/HIGH finding blocks every PR in that repo until it is fixed. Flip its +# `exit-code` to "0" to make Trivy visibility-only if that is too strict. +name: Security Scan + +on: + pull_request: + branches: [main, master, develop] + +permissions: + actions: read + contents: read + security-events: write + +jobs: + osv-scan: + # ponytail: reuse upstream diff-scoped PR scanner instead of hand-rolling it + uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@9a498708959aeaef5ef730655706c5a1df1edbc2 # v2.3.8 + permissions: + actions: read + contents: read + security-events: write + with: + fail-on-vuln: true + + dependency-review: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Dependency review + uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 + with: + fail-on-severity: high + comment-summary-in-pr: on-failure + + trivy-fs: + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + actions: read + steps: + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Trivy filesystem scan + uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0 + with: + scan-type: fs + scan-ref: . + scanners: vuln,secret,misconfig + severity: CRITICAL,HIGH + ignore-unfixed: true + format: sarif + output: trivy-results.sarif + exit-code: "1" + - name: Upload Trivy SARIF to code scanning + if: always() && hashFiles('trivy-results.sarif') != '' + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + sarif_file: trivy-results.sarif + category: trivy-fs + + scorecard: + runs-on: ubuntu-latest + # SOFT: posture findings are unrelated to the PR diff, so never block merge. + continue-on-error: true + permissions: + security-events: write + contents: read + actions: read + steps: + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Run Scorecard + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + publish_results: false + - name: Upload Scorecard SARIF to code scanning + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + sarif_file: results.sarif + category: scorecard