From ca23f2876ca8e8cc65fec47fa6a42b876c390795 Mon Sep 17 00:00:00 2001 From: Patrick Buckley Date: Wed, 1 Jul 2026 21:30:45 -0700 Subject: [PATCH] fix(ci): refuse fork PRs in the vendor-js dispatch path The workflow_dispatch input is an arbitrary PR number, and the job used only headRefName to pick the checkout ref. For a fork PR that is a bare branch name that can collide with a branch in this repo, so the job (contents:write, ends in git push) would operate on that unrelated branch. Resolve isCrossRepository alongside headRefName and fail loudly unless the PR head lives in this repository. --- .github/workflows/vendor-js.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/vendor-js.yml b/.github/workflows/vendor-js.yml index 644d2915..da014a62 100644 --- a/.github/workflows/vendor-js.yml +++ b/.github/workflows/vendor-js.yml @@ -46,7 +46,16 @@ jobs: PR_NUMBER: ${{ inputs.pr_number }} run: | if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then - ref=$(gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json headRefName -q .headRefName) + # The dispatch input is an arbitrary PR number; refuse fork PRs. + # A fork's headRefName is a bare branch name that may collide + # with a branch in this repo, and checkout+push would then hit + # that unrelated branch ("same-repo PRs only" applies here too). + pr_json=$(gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json headRefName,isCrossRepository) + if [[ "$(jq -r '.isCrossRepository' <<< "$pr_json")" != "false" ]]; then + echo "::error::PR #${PR_NUMBER} head is not a branch in this repository; refusing to complete it." + exit 1 + fi + ref=$(jq -r '.headRefName' <<< "$pr_json") else ref="$HEAD_REF" fi