From 1e69b49255891100892b5e1d0a3e8f30e427e6fb Mon Sep 17 00:00:00 2001 From: Mohammed Sufiyan Ahmed Date: Fri, 11 Sep 2026 17:25:13 +0530 Subject: [PATCH] Fix `Schema Diff` CI failing to post comment on PRs from forks (#20207) Fixes: #20167 The `Schema Diff` workflow posts a PR comment showing the effective schema diff. For PRs from forks, `GITHUB_TOKEN` is downgraded to read-only, so the comment-posting step was silently failing. ### Changes - `schema_diff.yml`: only post the comment directly when the PR is from the same repository. For forked PRs, upload the diff (and PR number) as a short-lived artifact instead of trying to comment. - `schema_diff_comment.yml` (new): triggered by `workflow_run` after `Schema Diff` completes, with `pull-requests: write` permission (granted because this workflow always runs in the context of the base repository). It downloads the artifact, if present, and posts the comment on behalf of the forked PR. This avoids `pull_request_target`, per the security concerns raised in the issue (zizmor flags it as dangerous). The new workflow only ever treats the downloaded artifact as inert comment text -- it is never executed. --------- Co-authored-by: Olivier 'reivilibre --- .github/workflows/schema_diff.yml | 29 ++++++++- .github/workflows/schema_diff_comment.yml | 71 +++++++++++++++++++++++ changelog.d/20207.misc | 1 + 3 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/schema_diff_comment.yml create mode 100644 changelog.d/20207.misc diff --git a/.github/workflows/schema_diff.yml b/.github/workflows/schema_diff.yml index f0d148b071..eaa25b53fa 100644 --- a/.github/workflows/schema_diff.yml +++ b/.github/workflows/schema_diff.yml @@ -1,4 +1,4 @@ -name: Schema Diff +name: Schema Diff # If this is changed, need to update `schema_diff_comment.yml` as well on: pull_request: @@ -95,9 +95,34 @@ jobs: echo "⚠️ Schema diff generation failed. See job logs for details." \ > "${{ runner.temp }}/schema_diff.md" + # Post a comment. + # + # For same-repo PRs, we can do this directly and we are done. - name: Post sticky PR comment uses: marocchino/sticky-pull-request-comment@3d7b8546315c63df45a03981d50a43ec19237f80 # v3 - if: always() + # Only run for same-repo PRs + if: always() && github.event.pull_request.head.repo.full_name == github.repository with: header: schema-diff path: ${{ runner.temp }}/schema_diff.md + + # For PRs from forks, we only have a read-only `GITHUB_TOKEN` here and need to work around + # by triggering a `workflow_run` onto the main repo. + # + # We upload an artifact and the `schema_diff_comment.yml` workflow will be triggered + # on the main repo by this workflow exiting. + - name: Save PR number for forked PRs + # Only run for fork PRs + if: always() && github.event.pull_request.head.repo.full_name != github.repository + run: echo "${{ github.event.pull_request.number }}" > "${{ runner.temp }}/pr_number" + + - name: Upload schema diff artifact for forked PRs + # Only run for fork PRs + if: always() && github.event.pull_request.head.repo.full_name != github.repository + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: schema-diff + path: | + ${{ runner.temp }}/schema_diff.md + ${{ runner.temp }}/pr_number + retention-days: 1 diff --git a/.github/workflows/schema_diff_comment.yml b/.github/workflows/schema_diff_comment.yml new file mode 100644 index 0000000000..4aefc3a123 --- /dev/null +++ b/.github/workflows/schema_diff_comment.yml @@ -0,0 +1,71 @@ +name: Post schema diff comment for forked PRs + +# The "Schema Diff" workflow can't post a PR comment for PRs from forks, +# since GITHUB_TOKEN is downgraded to read-only in that context. Instead it +# uploads the diff (and PR number) as an artifact, which we pick up here and +# post as a comment. +# +# This workflow runs in the context of the main repository, so its +# `GITHUB_TOKEN` can have write access. +# The downloaded artifact comes from an untrusted fork though, so it must only +# ever be treated as inert comment text, never executed. +# The PR number it carries is likewise untrusted: we cross-check it against the trusted +# `workflow_run` event before posting, so a fork PR can't point the comment +# at some other PR or issue. +on: + workflow_run: + workflows: ["Schema Diff"] + types: [completed] + +# DANGER: Due to untrusted input and untrusted triggers, +# think very carefully before expanding the permissions +permissions: + # To read the artifacts from the `schema_diff.yml` run + actions: read + # To post a comment to the PR + pull-requests: write + +jobs: + comment: + name: Post schema diff comment + runs-on: ubuntu-latest + # Only run when triggered by a pull request and only on a PR from a fork + if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.head_repository.full_name != github.repository && github.event.workflow_run.conclusion != 'cancelled' + steps: + - name: Download schema diff artifact + id: download + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: schema-diff + path: schema-diff + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + # The artifact (including the PR number) comes from an untrusted fork. + # Cross-check the number against the trusted workflow_run event before + # posting, so a fork PR cannot target another PR or issue. + - name: Verify PR matches the run that produced the artifact + id: verify + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # No checkout in this job, so tell gh which repo to talk to. + GH_REPO: ${{ github.repository }} + EXPECTED_BRANCH: ${{ github.event.workflow_run.head_branch }} + EXPECTED_HEAD_REPO: ${{ github.event.workflow_run.head_repository.full_name }} + run: | + pr_number="$(cat schema-diff/pr_number)" + read -r pr_branch pr_head_repo < <( + gh api "repos/{owner}/{repo}/pulls/$pr_number" --jq '.head.ref + " " + .head.repo.full_name' + ) + if [ "$pr_branch" != "$EXPECTED_BRANCH" ] || [ "$pr_head_repo" != "$EXPECTED_HEAD_REPO" ]; then + echo "::error::Artifact PR $pr_number does not match the triggering run; refusing to post a comment" + exit 1 + fi + echo "number=$pr_number" >> "$GITHUB_OUTPUT" + + - name: Post sticky PR comment + uses: marocchino/sticky-pull-request-comment@3d7b8546315c63df45a03981d50a43ec19237f80 # v3 + with: + header: schema-diff + number_force: ${{ steps.verify.outputs.number }} + path: schema-diff/schema_diff.md diff --git a/changelog.d/20207.misc b/changelog.d/20207.misc new file mode 100644 index 0000000000..3709ad46f1 --- /dev/null +++ b/changelog.d/20207.misc @@ -0,0 +1 @@ +Fix the `Schema Diff` CI check failing to post a comment on pull requests from forks.