/ .github / workflows / scorecard-enforcer.yml
scorecard-enforcer.yml
 1  # SPDX-License-Identifier: AGPL-3.0-or-later
 2  # Prevention workflow - runs OpenSSF Scorecard and fails on low scores
 3  name: OpenSSF Scorecard Enforcer
 4  
 5  on:
 6    push:
 7      branches: [main]
 8    schedule:
 9      - cron: '0 6 * * 1'  # Weekly on Monday
10    workflow_dispatch:
11  
12  permissions: read-all
13  
14  jobs:
15    scorecard:
16      runs-on: ubuntu-latest
17      permissions:
18        security-events: write
19        id-token: write  # For OIDC
20      steps:
21        - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
22          with:
23            persist-credentials: false
24  
25        - name: Run Scorecard
26          uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
27          with:
28            results_file: results.sarif
29            results_format: sarif
30            publish_results: true
31  
32        - name: Upload SARIF
33          uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3
34          with:
35            sarif_file: results.sarif
36  
37        - name: Check minimum score
38          run: |
39            # Parse score from results
40            SCORE=$(jq -r '.runs[0].tool.driver.properties.score // 0' results.sarif 2>/dev/null || echo "0")
41  
42            echo "OpenSSF Scorecard Score: $SCORE"
43  
44            # Minimum acceptable score (0-10 scale)
45            MIN_SCORE=5
46  
47            if [ "$(echo "$SCORE < $MIN_SCORE" | bc -l)" = "1" ]; then
48              echo "::error::Scorecard score $SCORE is below minimum $MIN_SCORE"
49              exit 1
50            fi
51  
52    # Check specific high-priority items
53    check-critical:
54      runs-on: ubuntu-latest
55      steps:
56        - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
57  
58        - name: Check SECURITY.md exists
59          run: |
60            if [ ! -f "SECURITY.md" ]; then
61              echo "::error::SECURITY.md is required"
62              exit 1
63            fi
64  
65        - name: Check for pinned dependencies
66          run: |
67            # Check workflows for unpinned actions
68            unpinned=$(grep -r "uses:.*@v[0-9]" .github/workflows/*.yml 2>/dev/null | grep -v "#" | head -5 || true)
69            if [ -n "$unpinned" ]; then
70              echo "::warning::Found unpinned actions:"
71              echo "$unpinned"
72            fi