ci(swift-sdk): add workflow permissions for PR comment posting#3158
ci(swift-sdk): add workflow permissions for PR comment posting#3158thepastaclaw wants to merge 1 commit intodashpay:v3.1-devfrom
Conversation
The Swift SDK build workflow's post-build step posts a PR comment with artifact/xcframework info using actions/github-script, which calls github.rest.issues.createComment. This requires issues: write and pull-requests: write permissions. Without explicit permissions, the workflow inherits the repository default (read-only), causing the comment step to fail with 403: 'Resource not accessible by integration'. This marks the entire CI check as failed even though the actual build succeeded. Add workflow-level permissions block with: - contents: read (required for checkout) - pull-requests: write (required for PR interaction) - issues: write (required for issues.createComment/updateComment API) Fixes the failing check on PR dashpay#3137.
📝 WalkthroughWalkthroughAdded explicit GitHub Actions workflow permissions (contents: read, pull-requests: write, issues: write) to the top-level permissions block in the swift-sdk-build.yml workflow file, enhancing security by declaring required access levels. Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~2 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
.github/workflows/swift-sdk-build.yml (1)
16-19:pull-requests: writeis not required by any step in this workflow.The comment step only calls
github.rest.issues.listComments,github.rest.issues.createComment, andgithub.rest.issues.updateComment— all of which requireissues: write, notpull-requests: write. The extra scope is harmless but violates least-privilege.♻️ Proposed fix
permissions: contents: read - pull-requests: write issues: write🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/swift-sdk-build.yml around lines 16 - 19, Remove the unnecessary "pull-requests: write" permission under the workflow's permissions block; keep "contents: read" and "issues: write" since the job only calls github.rest.issues.* methods, and ensure the permissions object only grants least-privilege (i.e., delete the pull-requests: write entry referencing the permissions block and value "pull-requests: write").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/swift-sdk-build.yml:
- Around line 16-19: The added permissions block (permissions: / contents: read
/ pull-requests: write / issues: write) won't grant write access for PRs from
external forks because GitHub downgrades GITHUB_TOKEN for forked PRs; fix by
implementing one of the recommended approaches: (1) split the workflow: keep the
current pull_request job for building/uploading artifacts (no write needed) and
add a separate workflow triggered by workflow_run in the base-repo context to
download the artifact and post comments, (2) enable the repo-level "Send write
tokens to workflows from pull requests" setting if acceptable, or (3) change the
trigger to pull_request_target only if you ensure the workflow does not check
out or execute untrusted PR code; reference the permissions block, issues:
write, pull_request trigger, workflow_run, and pull_request_target when making
the change.
---
Nitpick comments:
In @.github/workflows/swift-sdk-build.yml:
- Around line 16-19: Remove the unnecessary "pull-requests: write" permission
under the workflow's permissions block; keep "contents: read" and "issues:
write" since the job only calls github.rest.issues.* methods, and ensure the
permissions object only grants least-privilege (i.e., delete the pull-requests:
write entry referencing the permissions block and value "pull-requests: write").
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| issues: write |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check if the dashpay/platform repo has PR `#3137` and its origin
cd /tmp && git clone --depth=1 https://github.com/dashpay/platform.git 2>&1 | head -20Repository: dashpay/platform
Length of output: 1475
🌐 Web query:
GitHub Actions permissions block fork pull request write access token restrictions official documentation
💡 Result:
- Default restriction for fork PRs: Workflows triggered by a pull request from a fork run with a read-only
GITHUB_TOKENand do not receive secrets (exceptGITHUB_TOKEN). [1] - Why “write” is blocked: Even if your workflow sets
permissions: write-all(or specificwritescopes), fork PR workflows typically still can’t get write access—the repo/admin setting overrides this. [2] - The admin/owner override (the main switch): A repo/org/enterprise admin can explicitly allow write-capable tokens by enabling “Send write tokens to workflows from pull requests” (and separately can allow secrets). [3]
- Important exception (
pull_request_target): If you usepull_request_target, GitHub grants read/write repo permission toGITHUB_TOKENeven when triggered from a public fork, because the workflow runs in the base repository context (this is powerful and risky if you execute untrusted PR code). [2]
Official docs to cite for this behavior:
- Fork PR read-only token + no secrets: GitHub “Events that trigger workflows” → Workflows in forked repositories [1]
permissionsbehavior + fork limitation +pull_request_targetread/write note: GitHub “Workflow syntax” [2]- Admin policy/setting “Send write tokens to workflows from pull requests”: GitHub “Enforcing policies for GitHub Actions…” [3]
Sources: [1] (docs.github.com) [2] (docs.github.com) [3] (docs.github.com)
Citations:
- 1: https://docs.github.com/enterprise-server%403.20/actions/automating-your-workflow-with-github-actions/events-that-trigger-workflows?utm_source=openai
- 2: https://docs.github.com/en/enterprise-cloud%40latest/actions/reference/workflows-and-actions/workflow-syntax?utm_source=openai
- 3: https://docs.github.com/enterprise-server%403.16/admin/policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-github-actions-in-your-enterprise?utm_source=openai
🌐 Web query:
GitHub pull_request_target event workflow write permissions fork security
💡 Result:
pull_request_target is the “privileged” PR trigger in GitHub Actions, and it’s risky with forks because it runs in the base repo context (trusted workflow from the base branch) but is triggered by activity coming from an untrusted fork PR.
What changes vs pull_request (fork security model)
pull_requestfrom forks:GITHUB_TOKENis read-only and no secrets are provided (safe-by-default for untrusted contributions). (docs.github.com)pull_request_target:GITHUB_TOKENis read/write by default (unless you restrict viapermissions:) and the workflow can access secrets, even when triggered from a fork. (docs.github.com)
The core vulnerability (“pwn request”)
If a pull_request_target workflow checks out the PR’s head commit/branch and then builds/tests/executes anything from it, an attacker can run code with base-repo privileges (exfiltrate secrets, push commits, create releases, poison caches, etc.). (docs.github.com)
When pull_request_target is appropriate
Use it only for actions that treat PR content as data, not executable code—e.g., labeling, commenting, triage, metadata updates. (docs.github.com)
Safer patterns
- Prefer split workflows: run untrusted CI on
pull_request, then do privileged actions in a separate workflow triggered byworkflow_run. (codeql.github.com) - Least-privilege tokens: explicitly set
permissions:(don’t rely on defaults), granting only what the job needs. (docs.github.com) - If you must gate privileged runs: require a maintainer action (for example, a “safe to test” label) before running anything sensitive—GitHub Security Lab notes this helps but can still have race-condition pitfalls. (securitylab.github.com)
If you share your workflow YAML (especially any actions/checkout usage and permissions: blocks), I can point out the exact fork-to-write escalation paths and how to restructure it safely.
Citations:
- 1: https://docs.github.com/enterprise-server%403.20/actions/automating-your-workflow-with-github-actions/events-that-trigger-workflows?utm_source=openai
- 2: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows?utm_source=openai
- 3: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows?utm_source=openai
- 4: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows?utm_source=openai
- 5: https://codeql.github.com/codeql-query-help/actions/actions-untrusted-checkout-high/?utm_source=openai
- 6: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows?utm_source=openai
- 7: https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/?utm_source=openai
🏁 Script executed:
# Check the swift-sdk-build.yml workflow file
cat -n /tmp/platform/.github/workflows/swift-sdk-build.yml | head -30Repository: dashpay/platform
Length of output: 996
🏁 Script executed:
# Look for PR `#3137` in git history/refs or check recent PRs
cd /tmp/platform && git log --all --oneline --grep="3137" | head -10Repository: dashpay/platform
Length of output: 42
🏁 Script executed:
# Check if we can get PR details using git
cd /tmp/platform && git show-ref | grep -i pull | head -20Repository: dashpay/platform
Length of output: 42
🏁 Script executed:
# Read lines 16-30 to see the permissions block area
cd /tmp/platform && sed -n '16,30p' .github/workflows/swift-sdk-build.ymlRepository: dashpay/platform
Length of output: 441
🏁 Script executed:
# Read lines 170-250 to see the comment step
cd /tmp/platform && sed -n '170,250p' .github/workflows/swift-sdk-build.ymlRepository: dashpay/platform
Length of output: 3677
🏁 Script executed:
# Check git log for recent changes to this workflow
cd /tmp/platform && git log --oneline -10 -- .github/workflows/swift-sdk-build.ymlRepository: dashpay/platform
Length of output: 140
🏁 Script executed:
# Try to find if there are any branches or tags related to PR `#3137`
cd /tmp/platform && git show-ref | grep -E "(3137|swift-sdk-build)" | head -20Repository: dashpay/platform
Length of output: 42
🏁 Script executed:
# Check if there's a PR branch or recent tag
cd /tmp/platform && git for-each-ref --sort=-creatordate --format='%(refname:short) %(creatordate:short)' refs/ | head -20Repository: dashpay/platform
Length of output: 129
The permissions block cannot grant write access for external fork PRs—this fix is incomplete.
GitHub's security model mandates that pull requests from public forks always receive a read-only GITHUB_TOKEN, regardless of the permissions block settings in the workflow. This means the added permissions block will only fix the 403 for PRs opened from same-repo branches (i.e., from collaborators with push access).
If PR #3137 originated from an external fork, the issues: write permission will be silently downgraded to read-only by GitHub, and the comment step will continue to fail with a 403.
To reliably post comments for fork PRs, use one of these approaches:
-
Split workflow (recommended): Create a separate
workflow_run-triggered job that runs in the base-repo context:- The
pull_requestjob builds artifacts and uploads them (no write needed). - A
workflow_runjob then runs after completion, retrieves the artifact, and posts the comment with write access.
- The
-
Repository setting: Enable "Send write tokens to workflows from pull requests" in the repo settings. This grants write permissions to fork PR workflows—note that this carries security implications, especially with self-hosted runners.
-
pull_request_targetevent: Usepull_request_targetinstead ofpull_requestto run in the base-repo context with write access. Only use this if the workflow does not check out and execute untrusted PR code, as it creates a "pwn request" vulnerability.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/swift-sdk-build.yml around lines 16 - 19, The added
permissions block (permissions: / contents: read / pull-requests: write /
issues: write) won't grant write access for PRs from external forks because
GitHub downgrades GITHUB_TOKEN for forked PRs; fix by implementing one of the
recommended approaches: (1) split the workflow: keep the current pull_request
job for building/uploading artifacts (no write needed) and add a separate
workflow triggered by workflow_run in the base-repo context to download the
artifact and post comments, (2) enable the repo-level "Send write tokens to
workflows from pull requests" setting if acceptable, or (3) change the trigger
to pull_request_target only if you ensure the workflow does not check out or
execute untrusted PR code; reference the permissions block, issues: write,
pull_request trigger, workflow_run, and pull_request_target when making the
change.
Summary
The Swift SDK build workflow's post-build step uses
actions/github-script@v7to post PR comments with artifact/xcframework info. It callsgithub.rest.issues.createCommentandgithub.rest.issues.updateComment, which requireissues: writepermission.Without an explicit
permissionsblock, the workflow inherits the repo default (read-only forGITHUB_TOKENon fork PRs), causing a 403 error that fails the entire CI check — even though the xcframework build itself succeeded.Changes
Added a workflow-level
permissionsblock to.github/workflows/swift-sdk-build.yml:contents: read— required foractions/checkoutpull-requests: write— required for PR interactionissues: write— required forissues.createComment/updateCommentAPI callsContext
Observed on PR #3137 where the Swift SDK build succeeded but the comment posting step 403'd, failing CI.
Summary by CodeRabbit
Release Notes
Chores
Note: This release contains only internal infrastructure updates with no user-facing changes.