From f94e7f1273fd66542db5f27b9fce418aca8e4bd6 Mon Sep 17 00:00:00 2001 From: "ondrej@mirtes.cz" Date: Sun, 1 Nov 2026 15:20:16 +0000 Subject: [PATCH] Fix findings of the workflow linters actionlint, octoscan, poutine and zizmor (auditor persona) now report nothing. - Check out without persisting credentials, except in create-tag, where rickstaa/action-create-tag pushes the tag with the bot token so that the push triggers release.yml - Default every workflow to no permissions and grant each job what it uses - Add concurrency: superseded pull request runs are cancelled, pushes to 2.4.x each keep their own run, and release workflows are queued, not cancelled - Pass matrix.branch and github.ref_name to scripts through env - Name the remaining anonymous jobs - Create the release with gh release create instead of softprops/action-gh-release - Download a pinned actionlint release and verify its checksum instead of piping a script from main into bash - Allow the reviewed actions from unverified creators in .github/poutine.yml and the repository secrets used outside environments in .github/zizmor.yml - Ignore octoscan's dangerous-checkout message: every flagged ref is a fixed branch or commit, and octoscan cannot ignore findings per file Amp-Thread-ID: https://ampcode.com/threads/T-01a197a6-ea3e-77f5-80d6-1bebc3252d55 --- .github/poutine.yml | 17 ++++++++++++ .github/workflows/apiref.yml | 15 ++++++++--- .github/workflows/backward-compatibility.yml | 5 ++++ .github/workflows/build.yml | 15 +++++++++++ .github/workflows/create-tag.yml | 11 ++++++++ .github/workflows/lint-workflows.yml | 27 +++++++++++++++++-- .github/workflows/lock-closed-issues.yml | 5 ++++ .github/workflows/merge-maintained-branch.yml | 10 +++++++ .github/workflows/release-toot.yml | 7 +++++ .github/workflows/release-tweet.yml | 7 +++++ .github/workflows/release.yml | 22 ++++++++++----- .github/workflows/send-pr.yml | 18 ++++++++++--- .github/workflows/test-phpstan-src.yml | 7 +++++ .github/workflows/test-rector-src.yml | 8 ++++++ .../test-slevomat-coding-standard.yml | 7 +++++ .github/zizmor.yml | 13 +++++++++ 16 files changed, 179 insertions(+), 15 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 00000000..de82edee --- /dev/null +++ b/.github/poutine.yml @@ -0,0 +1,17 @@ +skip: + # Actions whose owners are not GitHub verified creators. Each of them is reviewed + # and pinned to a full commit SHA in the workflows that use it. + - rule: github_action_from_unverified_creator_used + purl: + - pkg:githubactions/astral-sh/setup-uv + - pkg:githubactions/cbrgm/mastodon-github-action + - pkg:githubactions/dessant/lock-threads + - pkg:githubactions/eomm/why-don-t-you-tweet + - pkg:githubactions/everlytic/branch-merge + - pkg:githubactions/metcalfc/changelog-generator + - pkg:githubactions/peter-evans/create-pull-request + - pkg:githubactions/rickstaa/action-create-tag + - pkg:githubactions/shivammathur/setup-php + - pkg:githubactions/synacktiv/action-octoscan + - pkg:githubactions/wyrihaximus/github-action-get-previous-tag + - pkg:githubactions/wyrihaximus/github-action-next-semvers diff --git a/.github/workflows/apiref.yml b/.github/workflows/apiref.yml index b5cbe64e..1cacec7e 100644 --- a/.github/workflows/apiref.yml +++ b/.github/workflows/apiref.yml @@ -7,6 +7,8 @@ on: branches: - "2.4.x" +permissions: {} + concurrency: group: "pages" cancel-in-progress: false @@ -16,6 +18,9 @@ jobs: name: "Build API References Pages" runs-on: "ubuntu-latest" + permissions: + contents: read # for actions/checkout to fetch the code + strategy: matrix: branch: @@ -35,6 +40,7 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false ref: ${{ matrix.branch }} - name: "Install PHP" @@ -53,7 +59,9 @@ jobs: run: "composer install --no-interaction --no-progress" - name: "Run ApiGen" - run: "apigen/vendor/bin/apigen -c apigen/apigen.neon --output docs/${{ matrix.branch }} -- src" + run: 'apigen/vendor/bin/apigen -c apigen/apigen.neon --output "docs/$BRANCH" -- src' + env: + BRANCH: ${{ matrix.branch }} - name: "Copy favicon" run: "cp apigen/favicon.png docs/favicon.png" @@ -87,13 +95,14 @@ jobs: path: 'docs' deploy: + name: "Deploy" needs: merge # from https://github.com/actions/deploy-pages permissions: - pages: write - id-token: write + pages: write # for actions/deploy-pages to deploy to GitHub Pages + id-token: write # for actions/deploy-pages to verify the deployment originates from an appropriate source environment: name: github-pages diff --git a/.github/workflows/backward-compatibility.yml b/.github/workflows/backward-compatibility.yml index 048114b6..1657936e 100644 --- a/.github/workflows/backward-compatibility.yml +++ b/.github/workflows/backward-compatibility.yml @@ -11,6 +11,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + jobs: backward-compatibility: name: "Backward Compatibility" @@ -27,6 +31,7 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false fetch-depth: 0 - name: "Install PHP" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a4601ab2..2b15b96c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,6 +11,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + jobs: lint: name: "Lint" @@ -35,6 +39,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 @@ -64,10 +70,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" @@ -122,6 +131,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 @@ -159,6 +170,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 @@ -202,6 +215,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml index 21a3e73a..811fc4d5 100644 --- a/.github/workflows/create-tag.yml +++ b/.github/workflows/create-tag.yml @@ -15,10 +15,18 @@ on: - patch - minor +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + jobs: create-tag: name: "Create tag" runs-on: "ubuntu-latest" + permissions: + contents: read # for actions/checkout to fetch the code; the tag is pushed with PHPSTAN_BOT_TOKEN steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 @@ -30,6 +38,9 @@ jobs: with: fetch-depth: 0 token: ${{ secrets.PHPSTAN_BOT_TOKEN }} + # rickstaa/action-create-tag pushes the tag with these credentials. A tag pushed by + # PHPSTAN_BOT_TOKEN, unlike one pushed by GITHUB_TOKEN, triggers release.yml. + persist-credentials: true # zizmor: ignore[artipacked] - name: 'Get Previous tag' id: previoustag diff --git a/.github/workflows/lint-workflows.yml b/.github/workflows/lint-workflows.yml index de442c20..418fe0f0 100644 --- a/.github/workflows/lint-workflows.yml +++ b/.github/workflows/lint-workflows.yml @@ -10,8 +10,13 @@ on: permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + jobs: actionlint: + name: Actionlint runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -20,12 +25,21 @@ jobs: egress-policy: audit - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Check workflow files run: | echo "::add-matcher::.github/actionlint-matcher.json" - bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) - ./actionlint -color + curl -fsSL -o "$RUNNER_TEMP/actionlint.tar.gz" "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" + echo "${ACTIONLINT_SHA256} $RUNNER_TEMP/actionlint.tar.gz" | sha256sum --check --strict + tar -xzf "$RUNNER_TEMP/actionlint.tar.gz" -C "$RUNNER_TEMP" actionlint + "$RUNNER_TEMP/actionlint" -color shell: bash + env: + ACTIONLINT_VERSION: "1.7.12" + # From actionlint_1.7.12_checksums.txt of the release + ACTIONLINT_SHA256: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" octoscan: name: Octoscan @@ -40,11 +54,20 @@ 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: '' + # octoscan treats every ref that is not made of letters and dashes only as custom, + # and our checkouts only use fixed branches and commits of trusted repositories + # (2.3.x, 2.x, a commit SHA, the matrix of our own branches). It cannot ignore + # findings per file, so this message is ignored everywhere. zizmor and poutine + # still check for checkouts of untrusted code. + ignore: "Use of 'actions/checkout' with a custom ref" - 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 690be32e..b2f09ca9 100644 --- a/.github/workflows/lock-closed-issues.yml +++ b/.github/workflows/lock-closed-issues.yml @@ -7,8 +7,13 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + 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/merge-maintained-branch.yml b/.github/workflows/merge-maintained-branch.yml index d929f010..6757bd49 100644 --- a/.github/workflows/merge-maintained-branch.yml +++ b/.github/workflows/merge-maintained-branch.yml @@ -7,10 +7,18 @@ on: branches: - "1.22.x" +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + jobs: merge: name: Merge branch runs-on: ubuntu-latest + permissions: + contents: read # for actions/checkout to fetch the code; the merge is made with PHPSTAN_BOT_TOKEN steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 @@ -19,6 +27,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Merge branch" uses: everlytic/branch-merge@c4a244dc23143f824ae6c022a10732566cb8e973 # 1.1.5 with: diff --git a/.github/workflows/release-toot.yml b/.github/workflows/release-toot.yml index e4a7f8e7..a3693c2d 100644 --- a/.github/workflows/release-toot.yml +++ b/.github/workflows/release-toot.yml @@ -6,8 +6,15 @@ on: release: types: [published] +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + 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 31674d5d..f7353745 100644 --- a/.github/workflows/release-tweet.yml +++ b/.github/workflows/release-tweet.yml @@ -6,8 +6,15 @@ on: release: types: [published] +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + 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 cdd09148..38719d8e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,10 +7,18 @@ on: tags: - '*' +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + jobs: deploy: name: "Deploy" runs-on: "ubuntu-latest" + permissions: + contents: read # for actions/checkout to fetch the code; the release is created with PHPSTAN_BOT_TOKEN steps: - name: Harden the runner (Audit all outbound calls) @@ -20,6 +28,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Generate changelog id: changelog @@ -28,9 +38,9 @@ jobs: myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} - name: "Create release" - uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 - with: - token: ${{ secrets.PHPSTAN_BOT_TOKEN }} - tag_name: ${{ github.ref_name }} - name: ${{ github.ref_name }} - body: ${{ steps.changelog.outputs.changelog }} + run: 'gh release create "$TAG" --verify-tag --title "$TAG" --notes "$CHANGELOG"' + env: + GH_TOKEN: ${{ secrets.PHPSTAN_BOT_TOKEN }} + GH_REPO: ${{ github.repository }} + TAG: ${{ github.ref_name }} + CHANGELOG: ${{ steps.changelog.outputs.changelog }} diff --git a/.github/workflows/send-pr.yml b/.github/workflows/send-pr.yml index 5f09a49a..3ca02f7b 100644 --- a/.github/workflows/send-pr.yml +++ b/.github/workflows/send-pr.yml @@ -6,8 +6,13 @@ on: release: types: [published] -permissions: - contents: read +# GITHUB_TOKEN needs no permissions: phpstan-src is checked out and the pull request +# is sent with PHPSTAN_BOT_TOKEN. +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false jobs: send-pr: @@ -28,6 +33,7 @@ jobs: - name: "Checkout phpstan-src" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false repository: phpstan/phpstan-src path: phpstan-src token: ${{ secrets.PHPSTAN_BOT_TOKEN }} @@ -39,9 +45,13 @@ jobs: - name: "Change composer.json" working-directory: ./phpstan-src - run: "composer require phpstan/phpdoc-parser:${{ github.ref_name }}" + run: 'composer require "phpstan/phpdoc-parser:$TAG"' + env: + TAG: ${{ github.ref_name }} - - name: "Create Pull Request" + # It commits, pushes a branch and opens the pull request in one step, which `gh pr create` + # alone does not do. + - name: "Create Pull Request" # zizmor: ignore[superfluous-actions] id: create-pr uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: diff --git a/.github/workflows/test-phpstan-src.yml b/.github/workflows/test-phpstan-src.yml index f9d7f5de..7b9372b7 100644 --- a/.github/workflows/test-phpstan-src.yml +++ b/.github/workflows/test-phpstan-src.yml @@ -11,6 +11,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + jobs: tests: name: "Tests" @@ -34,10 +38,13 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Checkout phpstan-src" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false repository: phpstan/phpstan-src path: phpstan-src ref: 2.3.x diff --git a/.github/workflows/test-rector-src.yml b/.github/workflows/test-rector-src.yml index a0be8049..93a332bc 100644 --- a/.github/workflows/test-rector-src.yml +++ b/.github/workflows/test-rector-src.yml @@ -11,6 +11,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + jobs: tests: name: "Tests" @@ -32,10 +36,13 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Checkout phpstan-src" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false repository: phpstan/phpstan-src path: phpstan-src ref: 2.3.x @@ -43,6 +50,7 @@ jobs: - name: "Checkout rector-src" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false repository: rectorphp/rector-src path: rector-src ref: main diff --git a/.github/workflows/test-slevomat-coding-standard.yml b/.github/workflows/test-slevomat-coding-standard.yml index d75dfa47..55489b3a 100644 --- a/.github/workflows/test-slevomat-coding-standard.yml +++ b/.github/workflows/test-slevomat-coding-standard.yml @@ -11,6 +11,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + jobs: tests: name: "Tests" @@ -36,10 +40,13 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Checkout Slevomat Coding Standard" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false repository: slevomat/coding-standard path: slevomat-cs ref: ae7325d76167f02e3b20b0ad19122dcf472188e4 diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 00000000..741f670d --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,13 @@ +rules: + secrets-outside-env: + config: + # These are repository secrets used by the release workflows (create-tag, release, + # send-pr, merge-maintained-branch, release-toot, release-tweet). Moving them into + # dedicated environments has to be done in the repository settings first. + allow: + - PHPSTAN_BOT_TOKEN + - MASTODON_ACCESS_TOKEN + - TWITTER_CONSUMER_API_KEY + - TWITTER_CONSUMER_API_SECRET + - TWITTER_ACCESS_TOKEN + - TWITTER_ACCESS_TOKEN_SECRET