From ccca7a84af68209138832cce030a224cd3d63522 Mon Sep 17 00:00:00 2001 From: Manuk Date: Wed, 19 Aug 2026 18:10:03 +0400 Subject: [PATCH 1/2] chore(ci): pin actions to commit SHAs and add dependabot cooldown Plumb's package audit fails two security checks for this package, which together hold the composite score at 82.16: security.actions-sha-pinned 0 of 12 refs pinned (weight 8) security.dependency-update-cooldown no cooldown config (weight 4) Tag refs are mutable: a compromised maintainer can re-point v7 at arbitrary code, as tj-actions/changed-files did in March 2025. Every third-party action is now pinned to a full 40-character commit SHA with the resolved version in a trailing comment, which is also what Dependabot reads to keep the pins current. The first-party reusable workflow ref (./.github/workflows/tests.yml) stays as-is; it is covered by branch protection. The dependabot cooldown adds a 7-day minimum age before an update is proposed, so an obviously malicious release has time to be caught before it lands. Matches filamentphp/filament and bezhanSalleh/filament-shield. Also adds a CI job that fails on any unpinned third-party ref, so this cannot silently regress, and pins tests.yml to a read-only token. --- .github/dependabot.yml | 6 +++++- .github/workflows/auto-merge.yml | 2 +- .github/workflows/changelog.yml | 6 +++--- .github/workflows/deploy-docs.yml | 6 +++--- .github/workflows/pint.yml | 6 +++--- .github/workflows/tests.yml | 32 +++++++++++++++++++++++++++++-- 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index be3564798..bd60816b7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,6 +8,8 @@ updates: directory: "/" schedule: interval: "weekly" + cooldown: + default-days: 7 labels: - "dependencies" @@ -15,5 +17,7 @@ updates: directory: "/docs" schedule: interval: "weekly" + cooldown: + default-days: 7 labels: - - "dependencies" \ No newline at end of file + - "dependencies" diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 7fc9639a0..401add46e 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -14,7 +14,7 @@ jobs: - name: Dependabot metadata id: metadata - uses: dependabot/fetch-metadata@v3.1.0 + uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 4e99bccc5..f4a1f61ab 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -25,19 +25,19 @@ jobs: echo "name=${BRANCH}" >> $GITHUB_OUTPUT - name: Checkout code - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ steps.branch.outputs.name }} - name: Update Changelog - uses: stefanzweifel/changelog-updater-action@v1 + uses: stefanzweifel/changelog-updater-action@a938690fad7edf25368f37e43a1ed1b34303eb36 # v1.12.0 with: latest-version: ${{ github.event.release.name }} release-notes: ${{ github.event.release.body }} - name: Open changelog PR id: cpr - uses: peter-evans/create-pull-request@v8 + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: base: ${{ steps.branch.outputs.name }} branch: chore/changelog-${{ github.event.release.tag_name }} diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index fd479202a..c6aea6064 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -65,18 +65,18 @@ jobs: esac - name: Checkout source branch - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ steps.version.outputs.branch }} - name: Checkout gh-pages - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: gh-pages path: gh-pages - name: Setup Node.js - uses: actions/setup-node@v7 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24' cache: 'npm' diff --git a/.github/workflows/pint.yml b/.github/workflows/pint.yml index 8f8f8c816..ad69628c1 100644 --- a/.github/workflows/pint.yml +++ b/.github/workflows/pint.yml @@ -14,14 +14,14 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.head_ref }} - name: Fix PHP code style issues - uses: aglipanci/laravel-pint-action@2.6 + uses: aglipanci/laravel-pint-action@36de00d5f5a8a4e12d443e01671daa12a18f4c79 # 2.6 - name: Commit changes - uses: stefanzweifel/git-auto-commit-action@v7 + uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0 with: commit_message: Fix styling diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 91955e2cf..cbd2e5d75 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,35 @@ on: branches: [4.x] workflow_call: +permissions: + contents: read + jobs: + actions-pinned: + name: Actions pinned to SHA + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Assert every third-party action is pinned to a full commit SHA + run: | + unpinned=$(grep -rhoE '^[[:space:]]*-?[[:space:]]*uses:[[:space:]]*[^[:space:]]+' .github/workflows \ + | sed -E 's/.*uses:[[:space:]]*//' \ + | grep -v '^\./' \ + | grep -vE '@[0-9a-f]{40}$' \ + | sort -u || true) + + if [ -n "$unpinned" ]; then + echo "Third-party actions must be pinned to a full 40-character commit SHA." + echo "Unpinned references:" + echo "$unpinned" | sed 's/^/ /' + exit 1 + fi + + echo "All third-party action references are pinned to a full commit SHA." + test: runs-on: ${{ matrix.os }} strategy: @@ -27,10 +55,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup PHP - uses: shivammathur/setup-php@v2 + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 with: php-version: ${{ matrix.php }} extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo From 42603a8f1b95f80221905a9d7c3918601d8a6c93 Mon Sep 17 00:00:00 2001 From: Manuk Date: Wed, 19 Aug 2026 18:22:50 +0400 Subject: [PATCH 2/2] chore(ci): adopt zizmor, fix its high findings, group dependabot PRs Follow-up to the SHA pinning work. Pins stop tag mutation but say nothing about the rest of the workflow surface, so this adds the static analyser the ecosystem has standardised on and clears everything it flags above low severity. zizmor 1.29.0 reported 5 high and 1 medium finding. All are fixed: dangerous-triggers auto-merge.yml ran on pull_request_target bot-conditions github.actor is spoofable as a bot check template-injection x2 release tag/branch interpolated into a run block excessive-permissions release.yml granted contents: write workflow-wide secrets-inherit release.yml passed every secret to tests.yml auto-merge.yml now follows GitHub's documented pattern: the pull_request trigger with permissions elevated per job, and the actor check reading github.event.pull_request.user.login. laravel/framework runs the same shape, so the elevated-permissions path is known to work for Dependabot. tests.yml consumes no secrets, so secrets: inherit was pure over-grant. zizmor itself runs via zizmorcore/zizmor-action, matching filamentphp/ filament. Results land in the Security tab; the pin gate stays the tests.yml job, which release.yml calls, so releases remain gated. Dependabot now groups action and npm bumps into one PR each instead of one per dependency, as laravel/framework does. Four artipacked and four low-confidence template-injection findings remain. The checkouts they name push commits and need their credentials. --- .github/dependabot.yml | 8 ++++++++ .github/workflows/auto-merge.yml | 12 ++++++++---- .github/workflows/changelog.yml | 14 +++++++++----- .github/workflows/release.yml | 5 +++-- .github/workflows/tests.yml | 4 ++++ .github/workflows/zizmor.yml | 30 ++++++++++++++++++++++++++++++ 6 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/zizmor.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bd60816b7..cf93bf94a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,6 +10,10 @@ updates: interval: "weekly" cooldown: default-days: 7 + groups: + github-actions: + patterns: + - "*" labels: - "dependencies" @@ -19,5 +23,9 @@ updates: interval: "weekly" cooldown: default-days: 7 + groups: + npm: + patterns: + - "*" labels: - "dependencies" diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 401add46e..e84727337 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -1,15 +1,19 @@ name: Auto-Merge -on: pull_request_target +on: pull_request permissions: - pull-requests: write - contents: write + contents: read jobs: dependabot: runs-on: ubuntu-latest - if: ${{ github.actor == 'dependabot[bot]' }} + if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} + + permissions: + pull-requests: write + contents: write + steps: - name: Dependabot metadata diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index f4a1f61ab..4cdc562a7 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -15,14 +15,17 @@ jobs: steps: - name: Determine target branch id: branch + env: + TAG: ${{ github.event.release.tag_name }} + REPO: ${{ github.repository }} + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} run: | - TAG="${{ github.event.release.tag_name }}" MAJOR=$(echo "$TAG" | sed -E 's/^v?([0-9]+)\..*/\1/') BRANCH="${MAJOR}.x" - if ! git ls-remote --exit-code --heads "https://github.com/${{ github.repository }}" "$BRANCH" > /dev/null 2>&1; then - BRANCH="${{ github.event.repository.default_branch }}" + if ! git ls-remote --exit-code --heads "https://github.com/$REPO" "$BRANCH" > /dev/null 2>&1; then + BRANCH="$DEFAULT_BRANCH" fi - echo "name=${BRANCH}" >> $GITHUB_OUTPUT + echo "name=${BRANCH}" >> "$GITHUB_OUTPUT" - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -54,4 +57,5 @@ jobs: if: steps.cpr.outputs.pull-request-operation == 'created' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh pr merge --auto --squash "${{ steps.cpr.outputs.pull-request-number }}" + PR_NUMBER: ${{ steps.cpr.outputs.pull-request-number }} + run: gh pr merge --auto --squash "$PR_NUMBER" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c5685095a..96c148264 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,16 +6,17 @@ on: - 'v*.*.*' permissions: - contents: write + contents: read jobs: tests: uses: ./.github/workflows/tests.yml - secrets: inherit release: needs: tests runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Determine release flags diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cbd2e5d75..2d358de63 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,6 +18,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Assert every third-party action is pinned to a full commit SHA run: | @@ -56,6 +58,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Setup PHP uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 000000000..c9e4493c4 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,30 @@ +name: Zizmor + +on: + push: + paths: + - '.github/workflows/**' + - '.github/dependabot.yml' + pull_request: + paths: + - '.github/workflows/**' + - '.github/dependabot.yml' + +permissions: + contents: read + +jobs: + zizmor: + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Audit workflows + uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2