From 468f80e6c54ca0f571b177789802642b5868e264 Mon Sep 17 00:00:00 2001 From: Dan Piazza <220388267+DanPiazza-Netwrix@users.noreply.github.com> Date: Fri, 22 May 2026 11:41:43 -0400 Subject: [PATCH 1/2] fix(claude-doc-pr): close untrusted-checkout alerts (#82, #61) Alert #82 (high): gate doc-review job on fork == false so the workflow skips entirely on fork PRs rather than running with limited token access. Alert #61 (high): capture headRefOid in pr-info and pin the doc-followup checkout to that SHA to close the TOCTOU race between the fork check and the actual checkout. Add git checkout -B to reattach to the named branch so Claude can push commits back without being in detached HEAD state. Generated with AI Co-Authored-By: Claude Code --- .github/workflows/claude-doc-pr.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/claude-doc-pr.yml b/.github/workflows/claude-doc-pr.yml index 108ec5ca4f..f98e6ddd75 100644 --- a/.github/workflows/claude-doc-pr.yml +++ b/.github/workflows/claude-doc-pr.yml @@ -16,7 +16,7 @@ on: jobs: doc-review: - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false concurrency: group: doc-pr-review-${{ github.event.pull_request.number }} cancel-in-progress: true @@ -143,10 +143,11 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | PR_NUMBER="${{ github.event.issue.number }}" - PR_DATA=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json headRefName,baseRefName,isCrossRepository) + PR_DATA=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json headRefName,baseRefName,isCrossRepository,headRefOid) BASE_BRANCH=$(echo "$PR_DATA" | jq -r '.baseRefName') echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT" echo "branch=$(echo "$PR_DATA" | jq -r '.headRefName')" >> "$GITHUB_OUTPUT" + echo "sha=$(echo "$PR_DATA" | jq -r '.headRefOid')" >> "$GITHUB_OUTPUT" echo "is_fork=$(echo "$PR_DATA" | jq -r '.isCrossRepository')" >> "$GITHUB_OUTPUT" # Check target branch using the shell variable to avoid # re-interpolating the output via expressions (code injection risk). @@ -176,10 +177,14 @@ jobs: if: steps.pr-info.outputs.is_fork == 'false' && steps.pr-info.outputs.targets_dev == 'true' uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: - ref: ${{ steps.pr-info.outputs.branch }} + ref: ${{ steps.pr-info.outputs.sha }} token: ${{ secrets.VALE_TOKEN }} fetch-depth: 0 + - name: Attach to branch + if: steps.pr-info.outputs.is_fork == 'false' && steps.pr-info.outputs.targets_dev == 'true' + run: git checkout -B "${{ steps.pr-info.outputs.branch }}" + - name: Handle @claude request if: steps.pr-info.outputs.is_fork == 'false' && steps.pr-info.outputs.targets_dev == 'true' uses: anthropics/claude-code-action@24492741e0ccfdef4c1d19da8e11e0f373d07494 # v1 From ded8fa7271fe18939885ad617086cca5641d7e58 Mon Sep 17 00:00:00 2001 From: Dan Piazza <220388267+DanPiazza-Netwrix@users.noreply.github.com> Date: Fri, 22 May 2026 11:44:36 -0400 Subject: [PATCH 2/2] fix(claude-doc-pr): pass branch name via env var to prevent code injection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git checkout -B used ${{ }} expression interpolation directly in the run block, which expands before the shell runs — shell quoting cannot protect against it. Pass the value through BRANCH env var instead, which is the standard GitHub Actions mitigation for this pattern. Generated with AI Co-Authored-By: Claude Code --- .github/workflows/claude-doc-pr.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/claude-doc-pr.yml b/.github/workflows/claude-doc-pr.yml index f98e6ddd75..61547359e3 100644 --- a/.github/workflows/claude-doc-pr.yml +++ b/.github/workflows/claude-doc-pr.yml @@ -183,7 +183,9 @@ jobs: - name: Attach to branch if: steps.pr-info.outputs.is_fork == 'false' && steps.pr-info.outputs.targets_dev == 'true' - run: git checkout -B "${{ steps.pr-info.outputs.branch }}" + env: + BRANCH: ${{ steps.pr-info.outputs.branch }} + run: git checkout -B "$BRANCH" - name: Handle @claude request if: steps.pr-info.outputs.is_fork == 'false' && steps.pr-info.outputs.targets_dev == 'true'