diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 00000000..1596d3a9 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,123 @@ +name: Security + +# Report-only in this plan: every scanner is non-blocking and uploads SARIF to +# the Security tab. Enforcement (fail on fixable HIGH/CRITICAL, block on secrets) +# is flipped on in Plan E after the remediation pass. + +on: + pull_request: + push: + branches: [main] + schedule: + - cron: '32 5 * * 1' # weekly Monday 05:32 UTC + +permissions: + contents: read + +concurrency: + group: security-${{ github.ref }} + cancel-in-progress: true + +jobs: + gitleaks: + name: Secret scan (gitleaks) + runs-on: ubuntu-latest + steps: + - name: Harden the runner + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + - name: Checkout (full history) + uses: actions/checkout@v5 + with: + fetch-depth: 0 # gitleaks scans the whole git history, not just the diff + - name: Run gitleaks + uses: gitleaks/gitleaks-action@v2 + continue-on-error: true # report-only; enforcement flipped on in Plan E + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + osv-scanner: + name: Dependency scan (OSV-Scanner) + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write # upload SARIF to code scanning + steps: + - name: Harden the runner + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + - name: Checkout + uses: actions/checkout@v5 + - name: Run OSV-Scanner + uses: google/osv-scanner-action@v2 + continue-on-error: true # report-only; enforcement flipped on in Plan E + with: + scan-args: |- + --lockfile=./pnpm-lock.yaml + --format=sarif + --output=osv-results.sarif + - name: Upload OSV SARIF + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: osv-results.sarif + category: osv-scanner + + trivy-config: + name: IaC/config scan (Trivy) + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - name: Harden the runner + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + - name: Checkout + uses: actions/checkout@v5 + - name: Trivy config scan (Dockerfile + docker-compose) + uses: aquasecurity/trivy-action@0.28.0 + continue-on-error: true # report-only; enforcement flipped on in Plan E + with: + scan-type: config + scan-ref: . + format: sarif + output: trivy-config.sarif + - name: Upload Trivy config SARIF + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-config.sarif + category: trivy-config + + trivy-license: + name: License compliance scan (Trivy) + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - name: Harden the runner + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + - name: Checkout + uses: actions/checkout@v5 + - name: Trivy license scan + uses: aquasecurity/trivy-action@0.28.0 + continue-on-error: true # report-only; enforcement flipped on in Plan E + with: + scan-type: fs + scan-ref: . + scanners: license + format: sarif + output: trivy-license.sarif + - name: Upload Trivy license SARIF + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-license.sarif + category: trivy-license