Skip to content

Commit 4c7e77f

Browse files
committed
ci: replace labeler pull_request_target with safe pull_request + workflow_run split
The labeler workflow used pull_request_target, which runs in the base-repo context with a write token and (for fork PRs) secret access. Per SOP this trigger is disallowed even though the workflow did not check out or run PR code. Split into two workflows following GitHub's recommended safe pattern: - labeler.yml (pull_request): untrusted context, read-only token, no secrets. Records only the PR number as an artifact. - labeler-apply.yml (workflow_run): trusted base-repo context with the write token. Downloads the PR number and applies labels via actions/labeler using its pr-number input. labeler reads the trusted base .github/labeler.yml and the PR's changed-file list via the API, so no PR-authored code executes. This preserves labeling on fork PRs (which a plain pull_request trigger cannot do). Helper actions pinned to commit SHAs. Signed-off-by: ravjotb <ravjot.brar@improving.com>
1 parent e5142c0 commit 4c7e77f

2 files changed

Lines changed: 66 additions & 5 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Label PRs (apply)
2+
3+
# Trusted half of the safe labeling split (see labeler.yml).
4+
# Runs in the base-repo context with a write token AFTER the untrusted
5+
# "Label PRs" workflow completes. It downloads the PR number that workflow
6+
# recorded and applies labels via actions/labeler. No PR-authored code is
7+
# checked out or executed here: labeler reads the trusted base-repo
8+
# .github/labeler.yml and the PR's changed-file list via the GitHub API.
9+
10+
on:
11+
workflow_run:
12+
workflows: ["Label PRs"]
13+
types: [completed]
14+
15+
permissions:
16+
contents: read
17+
pull-requests: write
18+
19+
jobs:
20+
apply-labels:
21+
runs-on: ubuntu-latest
22+
# Only act on successful runs that actually produced the artifact.
23+
if: >
24+
github.event.workflow_run.event == 'pull_request'
25+
&& github.event.workflow_run.conclusion == 'success'
26+
steps:
27+
- name: Download PR number artifact
28+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
29+
with:
30+
name: pr-number
31+
run-id: ${{ github.event.workflow_run.id }}
32+
github-token: ${{ secrets.GITHUB_TOKEN }}
33+
34+
- name: Read PR number
35+
id: pr
36+
run: |
37+
number="$(cat pr-number.txt)"
38+
# Guard: must be a positive integer before we trust it downstream.
39+
if ! [[ "$number" =~ ^[0-9]+$ ]]; then
40+
echo "Invalid PR number: '$number'" >&2
41+
exit 1
42+
fi
43+
echo "number=$number" >> "$GITHUB_OUTPUT"
44+
45+
- name: Apply labels
46+
uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7
47+
with:
48+
repo-token: ${{ secrets.GITHUB_TOKEN }}
49+
pr-number: ${{ steps.pr.outputs.number }}

.github/workflows/labeler.yml

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
11
name: Label PRs
22

3+
# Safe two-workflow labeling (avoids pull_request_target):
4+
# 1. This workflow runs in the UNTRUSTED pull_request context with a read-only
5+
# token and no secrets. It does not check out or run PR code — it only
6+
# records the PR number as an artifact.
7+
# 2. `labeler-apply.yml` runs on workflow_run (trusted base-repo context) with
8+
# the write token and applies the labels. This preserves labeling on fork
9+
# PRs, which a plain pull_request trigger cannot do on its own.
10+
311
on:
4-
pull_request_target:
12+
pull_request:
513
types: [opened, synchronize]
614

715
permissions:
816
contents: read
9-
pull-requests: write
1017

1118
jobs:
12-
label:
19+
record-pr:
1320
runs-on: ubuntu-latest
1421
steps:
15-
- uses: actions/labeler@v7
22+
- name: Save PR number
23+
run: echo "${{ github.event.pull_request.number }}" > pr-number.txt
24+
- name: Upload PR number artifact
25+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
1626
with:
17-
repo-token: ${{ secrets.GITHUB_TOKEN }}
27+
name: pr-number
28+
path: pr-number.txt
29+
retention-days: 1

0 commit comments

Comments
 (0)