diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index dc5da06..a6ca973 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -4,39 +4,39 @@ version: 'v0.0.2' workflows: '.github/workflows/boj-build.yml': - - 'actions/checkout@v7.0.1' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' '.github/workflows/casket-pages.yml': - - 'actions/checkout@v7.0.1' - - 'actions/configure-pages@v6.0.0' - - 'actions/deploy-pages@v5.0.1' - - 'actions/upload-pages-artifact@v5.0.0' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d' + - 'actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346' + - 'actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9' '.github/workflows/codeql.yml': - - 'actions/checkout@v7.0.1' - - 'github/codeql-action@b96794f015dfd88f77b49b1c93e0fa7110f94c63' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'github/codeql-action@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2' '.github/workflows/governance.yml': - - 'hyperpolymath/standards@81dbf2dd854b1444fd6236fa2352474383b2c2b9' + - 'hyperpolymath/standards@6e98f4bf810afb9b03d363b4c2c0c0adc1ac4953' '.github/workflows/hypatia-scan.yml': - 'hyperpolymath/standards@81dbf2dd854b1444fd6236fa2352474383b2c2b9' '.github/workflows/instant-sync.yml': - - 'peter-evans/repository-dispatch@v4.0.1' + - 'peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697' '.github/workflows/label-triage.yml': [] '.github/workflows/labels.yml': [] '.github/workflows/lock-sync-gate.yml': [] '.github/workflows/mirror.yml': - 'hyperpolymath/standards@0ef5917b3a6742dc69b6758b3e40a7beb8b97d04' '.github/workflows/pages.yml': - - 'actions/checkout@v7.0.1' - - 'actions/deploy-pages@v5.0.1' - - 'actions/upload-pages-artifact@v5.0.0' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346' + - 'actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9' '.github/workflows/push-email-notify.yml': - - 'hyperpolymath/smtp-notify-action@v0.3.0' + - 'hyperpolymath/smtp-notify-action@22e7bdb322c430c1d0dac6b3bb307f4bb139d0be' '.github/workflows/quality-gates.yml': - - 'actions/checkout@v7.0.1' - - 'dtolnay/rust-toolchain@v1' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de' '.github/workflows/secret-scanner.yml': - 'hyperpolymath/standards@c65436ee3351cd6b0fa14b142938b195efc77586' '.github/workflows/workflow-linter.yml': - - 'actions/checkout@v7.0.1' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' dependencies: 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9': ref: '55cc8345863c7cc4c66a329aec7e433d2d1c52a9' @@ -53,17 +53,12 @@ dependencies: commit: 'sha1-9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0' owner_id: 44036562 repo_id: 197814629 - 'actions/checkout@v7.0.1': - ref: 'v7.0.1' - commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' - owner_id: 44036562 - repo_id: 197814629 - 'actions/configure-pages@v6.0.0': + 'actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d': ref: 'v6.0.0' commit: 'sha1-45bfe0192ca1faeb007ade9deae92b16b8254a0d' owner_id: 44036562 repo_id: 513659658 - 'actions/deploy-pages@v5.0.1': + 'actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346': ref: 'v5.0.1' commit: 'sha1-368f82528645a54fb793d4d04e342629a3f51346' owner_id: 44036562 @@ -78,7 +73,7 @@ dependencies: commit: 'sha1-bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' owner_id: 44036562 repo_id: 192625955 - 'actions/upload-pages-artifact@v5.0.0': + 'actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9': ref: 'v5.0.0' commit: 'sha1-fc324d3547104276b827a68afc52ff2a11cc49c9' owner_id: 44036562 @@ -90,16 +85,21 @@ dependencies: commit: 'sha1-22d081ff2d3a40755e97629de92e3bcbfa7cf2ed' owner_id: 42048915 repo_id: 356423100 + 'dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de': + ref: 'v1' + commit: 'sha1-02cb101ec7c40f2c49e1d9714d64511d8e1b74de' + owner_id: 1940490 + repo_id: 260749683 'dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772': ref: '6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' commit: 'sha1-6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' owner_id: 1940490 repo_id: 260749683 - 'dtolnay/rust-toolchain@v1': - ref: 'v1' - commit: 'sha1-02cb101ec7c40f2c49e1d9714d64511d8e1b74de' - owner_id: 1940490 - repo_id: 260749683 + 'editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393': + ref: 'v3.0.0' + commit: 'sha1-51f63319f592f97930c73d9c46184d20bd206393' + owner_id: 26415196 + repo_id: 297874902 'editorconfig-checker/action-editorconfig-checker@840e866d93b8e032123c23bac69dece044d4d84c': ref: '840e866d93b8e032123c23bac69dece044d4d84c' commit: 'sha1-840e866d93b8e032123c23bac69dece044d4d84c' @@ -110,9 +110,9 @@ dependencies: commit: 'sha1-54075bcc5e249e4758d363f27d099f55d843f124' owner_id: 47606891 repo_id: 331103973 - 'github/codeql-action@b96794f015dfd88f77b49b1c93e0fa7110f94c63': - ref: 'v4.38.0' - commit: 'sha1-b96794f015dfd88f77b49b1c93e0fa7110f94c63' + 'github/codeql-action@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2': + ref: '2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2' + commit: 'sha1-2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2' owner_id: 9919 repo_id: 259445878 'github/codeql-action@7188fc363630916deb702c7fdcf4e481b751f97a': @@ -120,7 +120,7 @@ dependencies: commit: 'sha1-7188fc363630916deb702c7fdcf4e481b751f97a' owner_id: 9919 repo_id: 259445878 - 'hyperpolymath/smtp-notify-action@v0.3.0': + 'hyperpolymath/smtp-notify-action@22e7bdb322c430c1d0dac6b3bb307f4bb139d0be': ref: 'v0.3.0' commit: 'sha1-22e7bdb322c430c1d0dac6b3bb307f4bb139d0be' owner_id: 6759885 @@ -134,6 +134,16 @@ dependencies: - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' - 'dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' - 'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555' + 'hyperpolymath/standards@6e98f4bf810afb9b03d363b4c2c0c0adc1ac4953': + ref: '6e98f4bf810afb9b03d363b4c2c0c0adc1ac4953' + commit: 'sha1-6e98f4bf810afb9b03d363b4c2c0c0adc1ac4953' + owner_id: 6759885 + repo_id: 1116521501 + uses: + - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393' + - 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124' 'hyperpolymath/standards@81dbf2dd854b1444fd6236fa2352474383b2c2b9': ref: '81dbf2dd854b1444fd6236fa2352474383b2c2b9' commit: 'sha1-81dbf2dd854b1444fd6236fa2352474383b2c2b9' @@ -154,7 +164,7 @@ dependencies: repo_id: 1116521501 uses: - 'actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0' - 'peter-evans/repository-dispatch@v4.0.1': + 'peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697': ref: 'v4.0.1' commit: 'sha1-28959ce8df70de7be546dd1250a005dd32156697' owner_id: 18365890 diff --git a/.github/workflows/boj-build.yml b/.github/workflows/boj-build.yml index 47ae046..8762dc7 100644 --- a/.github/workflows/boj-build.yml +++ b/.github/workflows/boj-build.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. name: BoJ Server Build Trigger on: push: @@ -11,7 +11,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Trigger BoJ Server (Casket/ssg-mcp) run: | # Send a secure trigger to boj-server to build this repository diff --git a/.github/workflows/casket-pages.yml b/.github/workflows/casket-pages.yml index 583c1f8..a2faeaf 100644 --- a/.github/workflows/casket-pages.yml +++ b/.github/workflows/casket-pages.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. # # Deploys the prebuilt static site committed under site/ — CI deliberately # performs NO build step. The previous casket-ssg (Haskell) build never @@ -29,13 +29,13 @@ jobs: timeout-minutes: 5 steps: - name: Checkout - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup Pages - uses: actions/configure-pages@v6.0.0 + uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 - name: Upload artifact - uses: actions/upload-pages-artifact@v5.0.0 + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 with: path: 'site' - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v5.0.1 + uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9a7c8f8..7112957 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. name: CodeQL on: @@ -31,16 +31,16 @@ jobs: build-mode: none steps: - name: Checkout repository - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Initialize CodeQL - uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) + uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 (4.38.1 blocked estate-wide; nexia-list#100) with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Autobuild - uses: github/codeql-action/autobuild@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) + uses: github/codeql-action/autobuild@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 (4.38.1 blocked estate-wide; nexia-list#100) - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) + uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 (4.38.1 blocked estate-wide; nexia-list#100) diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 82c3a76..2728cc3 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. name: Governance on: @@ -15,4 +15,4 @@ permissions: jobs: governance: - uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@81dbf2dd854b1444fd6236fa2352474383b2c2b9 + uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@6e98f4bf810afb9b03d363b4c2c0c0adc1ac4953 diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 4592246..55d8df6 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. name: Hypatia Security Scan on: diff --git a/.github/workflows/instant-sync.yml b/.github/workflows/instant-sync.yml index 8e6971a..c593bf7 100644 --- a/.github/workflows/instant-sync.yml +++ b/.github/workflows/instant-sync.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. # Instant Forge Sync - Triggers propagation to all forges on push/release name: Instant Sync @@ -19,7 +19,7 @@ jobs: timeout-minutes: 15 steps: - name: Trigger Propagation - uses: peter-evans/repository-dispatch@v4.0.1 + uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 with: token: ${{ secrets.FARM_DISPATCH_TOKEN }} repository: hyperpolymath/.git-private-farm diff --git a/.github/workflows/label-triage.yml b/.github/workflows/label-triage.yml index 814a192..fc79947 100644 --- a/.github/workflows/label-triage.yml +++ b/.github/workflows/label-triage.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. name: Label Triage # Classify newly-filed issues against the estate label taxonomy. diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index 83ab941..af34c6b 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. name: Labels # Applies the canonical estate label set from .github/labels.json. diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index 9570b4a..80e03c0 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. name: Mirror to Git Forges on: push: diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 71d0e28..11d401c 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. name: GitHub Pages (Ddraig SSG) on: push: @@ -21,9 +21,9 @@ jobs: image: ghcr.io/stefan-hoeck/idris2-pack@sha256:f0758996a931fb35d9ecb1de273c4d59dabe2a09b433afc7e357f65a08b7e1ff steps: - name: Checkout Site - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Checkout Ddraig SSG - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: hyperpolymath/ddraig-ssg path: .ddraig-ssg @@ -40,7 +40,7 @@ jobs: fi ./.ddraig-ssg/build/exec/ddraig build src _site https://hyperpolymath.github.io/${GITHUB_REPOSITORY#*/} - name: Upload artifact - uses: actions/upload-pages-artifact@v5.0.0 + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 with: path: '_site' deploy: @@ -53,4 +53,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v5.0.1 + uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1 diff --git a/.github/workflows/push-email-notify.yml b/.github/workflows/push-email-notify.yml index 8a96bf9..5bba600 100644 --- a/.github/workflows/push-email-notify.yml +++ b/.github/workflows/push-email-notify.yml @@ -1,6 +1,6 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. +# This workflow is managed by gh actions-lock. # Dormant push-email notification. ARMED by setting the repo variable # PUSH_EMAIL_ENABLED=true (the single on/off switch). Addresses are pre-filled; # sending needs the org SMTP secrets (SMTP_HOST/PORT/USER/PASS). Inherited by @@ -41,7 +41,7 @@ jobs: timeout-minutes: 5 steps: - name: Send push notification email - uses: hyperpolymath/smtp-notify-action@v0.3.0 + uses: hyperpolymath/smtp-notify-action@22e7bdb322c430c1d0dac6b3bb307f4bb139d0be # v0.3.0 with: server_address: ${{ secrets.SMTP_HOST }} server_port: ${{ secrets.SMTP_PORT }} diff --git a/.github/workflows/quality-gates.yml b/.github/workflows/quality-gates.yml index fe449eb..a9c2fb1 100644 --- a/.github/workflows/quality-gates.yml +++ b/.github/workflows/quality-gates.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. name: Quality Gates permissions: actions: read @@ -15,7 +15,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install system dependencies run: | sudo apt-get update @@ -29,15 +29,10 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Rust toolchain - # v1 tag, not a SHA: dtolnay force-moves toolchain branches, so no - # symbolic ref contains the old pin and lockfile generation refuses it. - # The actions.lock records the resolved SHA; the toolchain must be an - # explicit input because the ref no longer selects it. - uses: dtolnay/rust-toolchain@v1 - with: - toolchain: v1 + # Pin the action independently of the requested Rust toolchain. + uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # v1 with: toolchain: stable - name: Install system dependencies diff --git a/.github/workflows/secret-scanner.yml b/.github/workflows/secret-scanner.yml index 8e4582a..1330e96 100644 --- a/.github/workflows/secret-scanner.yml +++ b/.github/workflows/secret-scanner.yml @@ -1,5 +1,5 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. name: Secret Scanner on: pull_request: diff --git a/.github/workflows/workflow-linter.yml b/.github/workflows/workflow-linter.yml index 41cb7bd..302c65b 100644 --- a/.github/workflows/workflow-linter.yml +++ b/.github/workflows/workflow-linter.yml @@ -1,50 +1,26 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. # Prevention workflow - validates all workflows have proper security config name: Workflow Security Linter on: pull_request: paths: - '.github/workflows/**' + - 'scripts/check-workflow-security.sh' + - 'tests/workflow-security.sh' push: paths: - '.github/workflows/**' + - 'scripts/check-workflow-security.sh' + - 'tests/workflow-security.sh' permissions: read-all jobs: lint-workflows: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@v7.0.1 - - name: Check SPDX headers - run: | - errors=0 - for f in .github/workflows/*.yml .github/workflows/*.yaml; do - [ -f "$f" ] || continue - if ! head -1 "$f" | grep -q "SPDX-License-Identifier"; then - echo "ERROR: $f missing SPDX header" - errors=$((errors + 1)) - fi - done - exit $errors - - name: Check permissions declaration - run: | - errors=0 - for f in .github/workflows/*.yml .github/workflows/*.yaml; do - [ -f "$f" ] || continue - if ! grep -q "^permissions:" "$f"; then - echo "ERROR: $f missing permissions declaration" - errors=$((errors + 1)) - fi - done - exit $errors - - name: Check pinned actions - run: | - errors=0 - for f in .github/workflows/*.yml .github/workflows/*.yaml; do - [ -f "$f" ] || continue - # Look for uses: without SHA - if grep -E "uses:.*@v[0-9]" "$f" | grep -v "#"; then - echo "WARNING: $f has unpinned actions (missing SHA comment)" - fi - done + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Test workflow security checker + run: bash tests/workflow-security.sh + - name: Check workflow security + run: bash scripts/check-workflow-security.sh diff --git a/docs/ISSUE-96-WORKFLOW-TRIAGE.adoc b/docs/ISSUE-96-WORKFLOW-TRIAGE.adoc new file mode 100644 index 0000000..3337cb9 --- /dev/null +++ b/docs/ISSUE-96-WORKFLOW-TRIAGE.adoc @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MPL-2.0 += Issue 96: workflow-check repair +:revdate: 2026-09-28 + +== Determination and default-branch evidence + +Both occurrences of `lint-workflows` in +https://github.com/hyperpolymath/vexometer/issues/96[issue 96] +refer to the same job, not separate implementations. +The determination for both is *repair*, not retirement or exemption. +The determination for `governance / Workflow security linter` is also *repair*. +These determinations describe the chosen remedy, not a claim that default-branch +acceptance has already been met. + +At default-branch commit `467e41b3a62f5a5ba78530a9f5c19ee12083bc3c`: + +* https://github.com/hyperpolymath/vexometer/actions/runs/36439803908[job lint-workflows] + failed at `Check SPDX headers`. +* https://github.com/hyperpolymath/vexometer/actions/runs/36439809171/job/108987051280[governance / Workflow security linter] + failed at `Check SPDX headers + permissions`. + +The GitHub job/step API confirms these failures; downloading the archived logs +failed in the development environment. Local reproduction established the causes +below. These are pre-existing failures on main, not defects introduced by PR 95. + +== Repairs + +* Restore first-line SPDX headers ahead of the actions-lock management comment. +* Replace action tags with the immutable commits already recorded in the lockfile. + Keep Rust's `stable` toolchain input independent of the action pin. +* Replace the local warning-only pin scan with a failing checker covering both + step actions and reusable-workflow calls. Regression tests exercise missing + headers, missing permissions, tag pins with misleading comments, quoted refs, + local actions, `.yaml` files, and empty directories. +* Upgrade governance from `81dbf2dd854b1444fd6236fa2352474383b2c2b9` to + `6e98f4bf810afb9b03d363b4c2c0c0adc1ac4953` (the observed upstream main). + The old pin recursively greps `actions.lock` and falsely reports its nested + `uses:` lists as unpinned actions. The updated workflow separates lockfile + verification, duplicate-key detection and upstream pin resolution. +* Synchronize the lockfile including the upgraded governance dependencies. + Preserve the existing CodeQL workflow commit and correct its stale lock record + and inaccurate version comment (the commit is v4.38.2, not v4.38.0). +* Remove the duplicate `with:` key in Quality Gates, which otherwise prevents + GitHub from parsing that workflow. + +No required-check settings were changed, no affected job was removed, and no +failure was demoted or bypassed. The new governance revision also carries other +upstream policy updates; any newly exposed failures should be triaged separately. + +== Validation and closure + +Local checks: + +[source,shell] +---- +bash tests/workflow-security.sh +bash scripts/check-workflow-security.sh +bash -n scripts/check-workflow-security.sh tests/workflow-security.sh +git diff --check +---- + +The upgraded governance's pinned duplicate-key and action-resolution helpers were +also executed locally: 15 workflow files clean, all 12 unique action pins resolve. +A separate strict YAML parse and structural lockfile audit checked workflow +coverage, exact per-workflow refs, and transitive dependency closure. + +The repository's GNU-awk lock-sync script could not run locally because gawk is +unavailable and Debian package downloads failed. Its GitHub run remains the +canonical validation; local structural validation is not a substitute for it. + +Leave issue 96 open until the repaired checks are green on the PR head and, +after merging, on a run of the default branch. Record those run URLs in the +issue. Do not treat a successful local test or a PR-only run as satisfying the +issue's default-branch acceptance criterion. diff --git a/scripts/check-workflow-security.sh b/scripts/check-workflow-security.sh new file mode 100755 index 0000000..3835b14 --- /dev/null +++ b/scripts/check-workflow-security.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# Keep first-line licensing, explicit permissions and immutable action refs mandatory. +set -euo pipefail +workflow_dir="${1:-.github/workflows}" +shopt -s nullglob +files=("$workflow_dir"/*.yml "$workflow_dir"/*.yaml) +if ((${#files[@]} == 0)); then + echo "ERROR: no workflows found in $workflow_dir" >&2 + exit 1 +fi +failed=0 +for file in "${files[@]}"; do + if ! head -1 "$file" | grep -q '^# SPDX-License-Identifier:'; then + echo "ERROR: $file missing first-line SPDX header" + failed=1 + fi + if ! grep -q '^permissions:' "$file"; then + echo "ERROR: $file missing top-level permissions declaration" + failed=1 + fi + # Match both step-level and job-level uses, including quoted scalar refs. + while IFS= read -r ref; do + case "$ref" in + ./*) continue ;; + docker://*@sha256:*) + if [[ "$ref" =~ @sha256:[a-f0-9]{64}$ ]]; then continue; fi ;; + *) + if [[ "$ref" =~ @[a-f0-9]{40}$ ]]; then continue; fi ;; + esac + echo "ERROR: $file has unpinned action: $ref" + failed=1 + done < <(sed -nE "s/^[[:space:]]*(-[[:space:]]+)?uses:[[:space:]]*['\"]?([^'\"[:space:]#]+).*/\2/p" "$file") +done +if ((failed)); then exit 1; fi +echo 'All workflows have first-line SPDX headers, permissions and immutable action refs' diff --git a/tests/workflow-security.sh b/tests/workflow-security.sh new file mode 100755 index 0000000..e1641f2 --- /dev/null +++ b/tests/workflow-security.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +set -euo pipefail +root="$(cd "$(dirname "$0")/.." && pwd)" +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT +checker="$root/scripts/check-workflow-security.sh" +expect_failure() { + if bash "$checker" "$tmp" > "$tmp/result" 2>&1; then + echo "FAIL: $1 was accepted" >&2 + exit 1 + fi +} +expect_failure 'empty workflow directory' +cat > "$tmp/test.yml" <<'YAML' +# SPDX-License-Identifier: MPL-2.0 +permissions: {contents: read} +jobs: + lint: + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + - uses: ./local-action +YAML +bash "$checker" "$tmp" +sed -i '1i# This workflow is managed by gh actions-lock.' "$tmp/test.yml" +expect_failure 'displaced SPDX header' +sed -i '1d' "$tmp/test.yml" +sed -i '/^permissions:/d' "$tmp/test.yml" +expect_failure 'missing permissions' +sed -i '2i permissions: read-all' "$tmp/test.yml" +sed -i 's/@3d3c42e5aac5ba805825da76410c181273ba90b1/@v7.0.1 # a comment is not a pin/' "$tmp/test.yml" +expect_failure 'tagged step action with comment' +cat > "$tmp/test.yml" <<'YAML' +# SPDX-License-Identifier: MPL-2.0 +permissions: {contents: read} +jobs: + shared: + uses: 'owner/repo/.github/workflows/check.yml@main' +YAML +expect_failure 'quoted unpinned reusable workflow' +sed -i 's/@main/@3d3c42e5aac5ba805825da76410c181273ba90b1/' "$tmp/test.yml" +mv "$tmp/test.yml" "$tmp/test.yaml" +bash "$checker" "$tmp" +echo 'Workflow security regression tests passed'