Skip to content

Commit 71dc547

Browse files
kdy1claude
andauthored
chore(ci): Refactor binary size workflow to use secure workflow_run pattern (#11203)
## Summary Refactored the binary size check workflow to follow [GitHub Security Lab best practices](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/) by splitting it into two workflows using the secure `workflow_run` pattern. ### Security Improvements **Before:** Single workflow using `pull_request` trigger with `pull-requests: write` permission - ❌ Untrusted PR code executed with write permissions - ❌ Potential privilege escalation attack vector - ❌ Risk of malicious PRs compromising the repository **After:** Two-workflow architecture with privilege separation - ✅ `binary-size.yml`: Runs on `pull_request` with read-only permissions, builds code and uploads artifacts - ✅ `binary-size-comment.yml`: Runs on `workflow_run` with write permissions, downloads artifacts and posts comments - ✅ Untrusted PR code never executes with write permissions or secrets access ### Changes 1. **binary-size.yml** (Build workflow) - Triggers on `pull_request` events - Permission: `contents: read` only - Builds binaries and measures sizes - Uploads results as workflow artifacts - Removed direct PR commenting logic 2. **binary-size-comment.yml** (Comment workflow) - Triggers on `workflow_run` completion - Permission: `pull-requests: write` - Downloads artifacts from build workflow - Posts/updates PR comments - Never executes PR code ### Why This Pattern? The `workflow_run` pattern ensures: - **Isolation**: PR code runs in unprivileged environment - **Token security**: Write tokens unavailable during PR code execution - **Defense in depth**: Even if PR code is malicious, it cannot access repository secrets or modify anything This follows the principle recommended by GitHub Security Lab: "untrusted PR data should be treated as untrusted when handled in privileged contexts." ## Test plan - [ ] Verify the build workflow runs successfully on PR events - [ ] Confirm artifacts are uploaded correctly - [ ] Check that the comment workflow triggers after build completion - [ ] Ensure PR comments are created/updated properly - [ ] Validate that no permissions errors occur 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <[email protected]>
1 parent c90e71f commit 71dc547

File tree

2 files changed

+63
-57
lines changed

2 files changed

+63
-57
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Binary Size Check - Comment
2+
3+
on:
4+
workflow_run:
5+
workflows: ["Binary Size Check - Build"]
6+
types:
7+
- completed
8+
9+
env:
10+
CI: 1
11+
12+
jobs:
13+
comment-on-pr:
14+
name: Comment on PR
15+
runs-on: ubuntu-latest
16+
# This workflow needs write permissions to comment on PRs
17+
# It's safe because it doesn't execute any code from the PR
18+
permissions:
19+
pull-requests: write
20+
contents: read
21+
# Only run on successful builds
22+
if: github.event.workflow_run.conclusion == 'success'
23+
steps:
24+
- name: Download artifact
25+
uses: actions/download-artifact@v4
26+
with:
27+
name: size-report
28+
github-token: ${{ secrets.GITHUB_TOKEN }}
29+
run-id: ${{ github.event.workflow_run.id }}
30+
31+
- name: Read PR number
32+
id: pr
33+
run: |
34+
PR_NUMBER=$(cat pr_number.txt)
35+
echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT
36+
37+
- name: Find existing comment
38+
uses: peter-evans/find-comment@v3
39+
id: find-comment
40+
with:
41+
issue-number: ${{ steps.pr.outputs.number }}
42+
comment-author: "github-actions[bot]"
43+
body-includes: "## Binary Sizes"
44+
45+
- name: Create or update PR comment
46+
uses: peter-evans/create-or-update-comment@v4
47+
with:
48+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
49+
issue-number: ${{ steps.pr.outputs.number }}
50+
body-path: size_report.md
51+
edit-mode: replace

.github/workflows/binary-size.yml

Lines changed: 12 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
name: Binary Size Check
1+
name: Binary Size Check - Build
22

33
on:
44
pull_request:
55
types: ["opened", "reopened", "synchronize"]
6-
# Disable push events for now because of the permission issue
7-
# push:
8-
# branches:
9-
# - main
106

117
env:
128
CI: 1
@@ -23,8 +19,8 @@ jobs:
2319
measure-binary-size:
2420
name: Measure Binary Size
2521
runs-on: ubuntu-latest
22+
# No special permissions needed - this workflow only reads and uploads artifacts
2623
permissions:
27-
pull-requests: write
2824
contents: read
2925
steps:
3026
- uses: actions/checkout@v4
@@ -77,56 +73,15 @@ jobs:
7773
echo "" >> size_report.md
7874
echo "*Commit: ${{ github.sha }}*" >> size_report.md
7975
80-
- name: Find existing comment
81-
if: github.event_name == 'pull_request'
82-
uses: peter-evans/find-comment@v3
83-
id: find-comment
84-
with:
85-
issue-number: ${{ github.event.pull_request.number }}
86-
comment-author: "github-actions[bot]"
87-
body-includes: "## Binary Sizes"
76+
- name: Save PR number
77+
run: |
78+
echo "${{ github.event.pull_request.number }}" > pr_number.txt
8879
89-
- name: Create or update PR comment
90-
if: github.event_name == 'pull_request'
91-
uses: peter-evans/create-or-update-comment@v4
80+
- name: Upload size report and PR info
81+
uses: actions/upload-artifact@v4
9282
with:
93-
comment-id: ${{ steps.find-comment.outputs.comment-id }}
94-
issue-number: ${{ github.event.pull_request.number }}
95-
body-path: size_report.md
96-
edit-mode: replace
97-
98-
- name: Find existing commit comment
99-
if: github.event_name == 'push'
100-
id: find-commit-comment
101-
run: |
102-
COMMENTS=$(gh api repos/${{ github.repository }}/commits/${{ github.sha }}/comments \
103-
--jq '.[] | select(.body | contains("## Binary Sizes")) | .id' | head -n 1)
104-
105-
if [ -n "$COMMENTS" ]; then
106-
echo "comment_id=$COMMENTS" >> $GITHUB_OUTPUT
107-
else
108-
echo "comment_id=" >> $GITHUB_OUTPUT
109-
fi
110-
env:
111-
GH_TOKEN: ${{ github.token }}
112-
113-
- name: Create or update commit comment
114-
if: github.event_name == 'push'
115-
run: |
116-
BODY=$(cat size_report.md)
117-
118-
if [ -n "${{ steps.find-commit-comment.outputs.comment_id }}" ]; then
119-
gh api \
120-
--method PATCH \
121-
-H "Accept: application/vnd.github+json" \
122-
repos/${{ github.repository }}/comments/${{ steps.find-commit-comment.outputs.comment_id }} \
123-
-f body="$BODY"
124-
else
125-
gh api \
126-
--method POST \
127-
-H "Accept: application/vnd.github+json" \
128-
repos/${{ github.repository }}/commits/${{ github.sha }}/comments \
129-
-f body="$BODY"
130-
fi
131-
env:
132-
GH_TOKEN: ${{ github.token }}
83+
name: size-report
84+
path: |
85+
size_report.md
86+
pr_number.txt
87+
retention-days: 1

0 commit comments

Comments
 (0)