|
| 1 | +name: "Check pinned GitHub Actions" |
| 2 | + |
| 3 | +on: |
| 4 | + pull_request: |
| 5 | + paths: |
| 6 | + - ".github/workflows/**" |
| 7 | + - ".github/actions/**" |
| 8 | + push: |
| 9 | + branches: |
| 10 | + - main |
| 11 | + paths: |
| 12 | + - ".github/workflows/**" |
| 13 | + - ".github/actions/**" |
| 14 | + |
| 15 | +permissions: {} |
| 16 | + |
| 17 | +jobs: |
| 18 | + check-pinned-actions: |
| 19 | + name: "Scan for unpinned actions" |
| 20 | + runs-on: ubuntu-latest |
| 21 | + permissions: |
| 22 | + contents: read |
| 23 | + steps: |
| 24 | + - name: "Checkout code" |
| 25 | + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 |
| 26 | + with: |
| 27 | + persist-credentials: false |
| 28 | + |
| 29 | + - name: "Install uv" |
| 30 | + uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 |
| 31 | + |
| 32 | + # Only fails on unpinned-uses: zizmor's other audits are out of scope for this check. |
| 33 | + - name: "Scan for unpinned actions" |
| 34 | + env: |
| 35 | + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 36 | + run: | |
| 37 | + TARGETS=".github/workflows" |
| 38 | + [ -d ".github/actions" ] && TARGETS="$TARGETS .github/actions" |
| 39 | +
|
| 40 | + uvx "zizmor@1.29.0" --format=json-v1 --no-exit-codes $TARGETS > zizmor-results.json |
| 41 | +
|
| 42 | + UNPINNED=$(jq '[.[] | select(.ident == "unpinned-uses")]' zizmor-results.json) |
| 43 | + COUNT=$(echo "$UNPINNED" | jq 'length') |
| 44 | +
|
| 45 | + if [ "$COUNT" -gt 0 ]; then |
| 46 | + echo "::error::Found $COUNT unpinned GitHub Action reference(s). All actions must be pinned to a full 40-character commit SHA." |
| 47 | + echo "$UNPINNED" | jq -r ' |
| 48 | + .[] as $finding | |
| 49 | + (($finding.locations[] | select(.symbolic.kind == "Primary")) // $finding.locations[0]) as $loc | |
| 50 | + " - \($loc.symbolic.key.Local.verbatim_path // $loc.concrete.feature // "unknown location"):\(($loc.concrete.location.start_point.row // 0) + 1): \($finding.desc)" |
| 51 | + ' |
| 52 | + echo "" |
| 53 | + echo "To fix: replace the tag/branch with the action's full commit SHA and add a '# vX.Y.Z' comment, e.g." |
| 54 | + echo " uses: owner/repo@<40-char-sha> # v1.2.3" |
| 55 | + echo "" |
| 56 | + echo "If a version genuinely cannot be pinned to a SHA, add an inline exception with justification:" |
| 57 | + echo " uses: owner/repo@ref # zizmor: ignore[unpinned-uses] <reason>" |
| 58 | + exit 1 |
| 59 | + fi |
| 60 | +
|
| 61 | + echo "No unpinned GitHub Actions found." |
0 commit comments