From 331d961f65447ae76edf3ed51254c19351aff340 Mon Sep 17 00:00:00 2001 From: "ondrej@mirtes.cz" Date: Sun, 1 Nov 2026 15:17:12 +0000 Subject: [PATCH] Fix GitHub Actions workflow linter findings Address actionlint, octoscan, poutine and zizmor findings: drop persisted checkout credentials, add explicit permissions and concurrency, install actionlint from a checksummed release, avoid template injection, and document intentional exceptions. --- .github/poutine.yml | 4 ++++ .github/workflows/build.yml | 17 ++++++++++++++++- .github/workflows/create-tag.yml | 10 +++++++++- .github/workflows/e2e-tests.yml | 6 ++++-- .github/workflows/lint-workflows.yml | 21 ++++++++++++++++++++- .github/workflows/lock-closed-issues.yml | 5 +++++ .github/workflows/release-toot.yml | 7 +++++++ .github/workflows/release-tweet.yml | 7 +++++++ .github/workflows/release.yml | 11 ++++++++++- .github/zizmor.yml | 11 +++++++++++ 10 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 .github/poutine.yml create mode 100644 .github/zizmor.yml diff --git a/.github/poutine.yml b/.github/poutine.yml new file mode 100644 index 0000000..e35805b --- /dev/null +++ b/.github/poutine.yml @@ -0,0 +1,4 @@ +skip: + # Every third-party action is pinned to a full commit SHA, which is what protects against + # a compromised upstream; whether the publisher is a "verified creator" adds nothing on top. + - rule: github_action_from_unverified_creator_used diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64af7dc..c24101d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,10 @@ on: branches: - "2.0.x" +concurrency: + group: build-${{ github.head_ref || github.run_id }} # will be canceled on subsequent pushes in pull requests but not branches + cancel-in-progress: true + permissions: contents: read @@ -39,6 +43,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 @@ -68,10 +74,13 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Checkout build-cs" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false repository: "phpstan/build-cs" path: "build-cs" ref: "2.x" @@ -129,6 +138,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 @@ -178,6 +189,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 @@ -212,6 +225,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 @@ -219,7 +234,7 @@ jobs: coverage: "none" php-version: "8.5" - - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 + - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # 4.0.0 - name: "Name Collision Detector" run: "make name-collision" diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml index 2abdb94..66d3e29 100644 --- a/.github/workflows/create-tag.yml +++ b/.github/workflows/create-tag.yml @@ -15,6 +15,13 @@ on: - patch - minor +concurrency: + group: create-tag + cancel-in-progress: true + +permissions: + contents: read + jobs: create-tag: name: "Create tag" @@ -25,7 +32,8 @@ jobs: with: egress-policy: audit - - name: "Checkout" + # Credentials must stay persisted: rickstaa/action-create-tag pushes the tag with them + - name: "Checkout" # zizmor: ignore[artipacked] uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 6e27a17..850ea79 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -38,6 +38,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # 2.40.0 @@ -45,7 +47,7 @@ jobs: coverage: "none" php-version: "8.3" - - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 + - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # 4.0.0 - name: "Install bashunit" uses: "TypedDevs/bashunit@8d4f357f52df7596f92ece2d6c049d91d45517a2" # 0.52.0 @@ -53,4 +55,4 @@ jobs: directory: "e2e" - name: "Test" - run: ${{ matrix.script }} + run: ${{ matrix.script }} # zizmor: ignore[template-injection] the script is defined by the matrix above diff --git a/.github/workflows/lint-workflows.yml b/.github/workflows/lint-workflows.yml index 318f688..84d0a8a 100644 --- a/.github/workflows/lint-workflows.yml +++ b/.github/workflows/lint-workflows.yml @@ -8,10 +8,15 @@ on: branches: - "2.0.x" +concurrency: + group: lint-workflows-${{ github.head_ref || github.run_id }} # will be canceled on subsequent pushes in pull requests but not branches + cancel-in-progress: true + permissions: {} jobs: actionlint: + name: actionlint runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -20,10 +25,18 @@ jobs: egress-policy: audit - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Check workflow files + env: + ACTIONLINT_VERSION: "1.7.12" + ACTIONLINT_SHA256: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" run: | echo "::add-matcher::.github/actionlint-matcher.json" - bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) + curl -fsSL -o actionlint.tar.gz "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" + echo "${ACTIONLINT_SHA256} actionlint.tar.gz" | sha256sum -c - + tar -xzf actionlint.tar.gz actionlint ./actionlint -color shell: bash @@ -40,11 +53,17 @@ jobs: egress-policy: audit - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Run octoscan id: octoscan uses: synacktiv/action-octoscan@6b1cf2343893dfb9e5f75652388bd2dc83f456b0 # v1.0.0 with: filter_triggers: '' + # The only custom-ref checkouts are phpstan/build-cs and phpstan/build-infection at fixed branches, + # and the only local action comes from the phpstan/build-infection checkout. + ignore: "Use of 'actions/checkout' with a custom ref|Use of local action .\\./build-infection/" - name: Upload SARIF file to GitHub uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1 diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml index 0718926..b4c60b0 100644 --- a/.github/workflows/lock-closed-issues.yml +++ b/.github/workflows/lock-closed-issues.yml @@ -4,11 +4,16 @@ on: schedule: - cron: '14 0 * * *' +concurrency: + group: lock-closed-issues + cancel-in-progress: true + permissions: contents: read jobs: lock: + name: "Lock closed issues" permissions: issues: write # for dessant/lock-threads to lock issues runs-on: ubuntu-latest diff --git a/.github/workflows/release-toot.yml b/.github/workflows/release-toot.yml index 4e8a264..0533729 100644 --- a/.github/workflows/release-toot.yml +++ b/.github/workflows/release-toot.yml @@ -6,8 +6,15 @@ on: release: types: [published] +concurrency: + group: toot-${{ github.event.release.tag_name }} + cancel-in-progress: true + +permissions: {} + jobs: toot: + name: "Toot release" runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) diff --git a/.github/workflows/release-tweet.yml b/.github/workflows/release-tweet.yml index eb7cd95..003b399 100644 --- a/.github/workflows/release-tweet.yml +++ b/.github/workflows/release-tweet.yml @@ -6,8 +6,15 @@ on: release: types: [published] +concurrency: + group: tweet-${{ github.event.release.tag_name }} + cancel-in-progress: true + +permissions: {} + jobs: tweet: + name: "Tweet release" runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d8ea4ed..1da62b7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,13 @@ on: tags: - '*' +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + jobs: deploy: name: "Deploy" @@ -20,6 +27,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Generate changelog id: changelog @@ -27,7 +36,7 @@ jobs: with: myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} - - name: "Create release" + - name: "Create release" # zizmor: ignore[superfluous-actions] uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 with: token: ${{ secrets.PHPSTAN_BOT_TOKEN }} diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000..0a8011d --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,11 @@ +rules: + secrets-outside-env: + config: + # Repository-level secrets used by release automation; they are not scoped to deployment environments. + allow: + - PHPSTAN_BOT_TOKEN + - MASTODON_ACCESS_TOKEN + - TWITTER_CONSUMER_API_KEY + - TWITTER_CONSUMER_API_SECRET + - TWITTER_ACCESS_TOKEN + - TWITTER_ACCESS_TOKEN_SECRET