diff --git a/.github/workflows/security-scan.yaml b/.github/workflows/security-scan.yaml new file mode 100644 index 0000000..10f21ac --- /dev/null +++ b/.github/workflows/security-scan.yaml @@ -0,0 +1,93 @@ +name: Security scan + +# Report-only: findings are written to the job summary and never fail the job. +# A scanner that fails to run fails its job, so a broken scan is not reported as clean. +# Tighten to blocking once the existing backlog is cleared. +# Secret scanning already runs in ci.yaml (gitleaks job), so it is not repeated here. + +on: + pull_request: + push: + branches: [main] + schedule: + - cron: "23 5 * * 1" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: security-scan-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + OSV_SCANNER_VERSION: "2.7.0" + # Bump the version and the hash together. + OSV_SCANNER_SHA256: 160c6253e2922e9e0a6a9771c5c4c40a4b3c6090b34a3cdd5e80c96cc2e4218b + ZIZMOR_VERSION: "1.31.1" + +jobs: + osv-scanner: + name: Dependency scan (osv-scanner) + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Install osv-scanner + run: | + set -euo pipefail + tool_dir="$RUNNER_TEMP/osv-scanner" + mkdir -p "$tool_dir" + cd "$tool_dir" + base="https://github.com/google/osv-scanner/releases/download/v${OSV_SCANNER_VERSION}" + curl -fsSL --retry 3 -o osv-scanner_linux_amd64 "$base/osv-scanner_linux_amd64" + echo "${OSV_SCANNER_SHA256} osv-scanner_linux_amd64" | sha256sum -c - + install -m 0755 osv-scanner_linux_amd64 osv-scanner + echo "$tool_dir" >> "$GITHUB_PATH" + + - name: Scan lockfiles + run: | + set -uo pipefail + report="$RUNNER_TEMP/osv.md" + status=0 + osv-scanner scan source -r --format markdown . > "$report" || status=$? + { + echo "## osv-scanner" + echo + # Exit 0: nothing found. 1: vulnerabilities found. 128: no packages found. Anything else: the scan failed. + case "$status" in + 0 | 1) head -c 900000 "$report" ;; + 128) echo "No packages found to scan." ;; + *) echo "osv-scanner failed with exit code $status. See the step log." ;; + esac + } >> "$GITHUB_STEP_SUMMARY" + case "$status" in 0 | 1 | 128) ;; *) exit "$status" ;; esac + + zizmor: + name: Workflow scan (zizmor) + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Audit workflows and local actions + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + report="$RUNNER_TEMP/zizmor.txt" + pipx run "zizmor==${ZIZMOR_VERSION}" --no-exit-codes --format plain .github > "$report" + { + echo "## zizmor" + echo + echo '```text' + head -c 900000 "$report" + echo '```' + } >> "$GITHUB_STEP_SUMMARY"