Files
nanoclaw/.github/workflows/label-pr.yml
T
glifocat 2b51a4e707 fix(workflows): label PRs from forks that follow the contributing template
On a fork PR, GITHUB_TOKEN is demoted to read-only regardless of the
workflow's permissions: block, so issues.addLabels() returns 403. The
label workflow silently works for PRs that skip the template (no
checkboxes ticked → no API call) and fails for PRs that actually
follow it — a hostile incentive against contributors who do the right
thing.

pull_request_target runs in the context of the base branch with full
declared permissions, which is the documented fix for this case. Safe
here because the workflow is metadata-only: it reads
context.payload.pull_request.body and calls addLabels. No checkout,
no PR-supplied code executes. A SECURITY comment is added above the
trigger to keep it that way.

Refs:
- https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request_target
- https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:50:25 +02:00

41 lines
1.7 KiB
YAML

name: Label PR
# SECURITY: this workflow runs with write access to the base repo on fork PRs,
# because `pull_request_target` executes in the context of the base branch.
# Keep it metadata-only — do NOT add actions/checkout or any step that
# executes PR-supplied content (install scripts, build commands, etc.).
# See https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/
on:
pull_request_target:
types: [opened, edited]
jobs:
label:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/github-script@v7
with:
script: |
const body = context.payload.pull_request.body || '';
const labels = [];
if (body.includes('[x] **Feature skill**')) { labels.push('PR: Skill'); labels.push('PR: Feature'); }
else if (body.includes('[x] **Utility skill**')) labels.push('PR: Skill');
else if (body.includes('[x] **Operational/container skill**')) labels.push('PR: Skill');
else if (body.includes('[x] **Fix**')) labels.push('PR: Fix');
else if (body.includes('[x] **Simplification**')) labels.push('PR: Refactor');
else if (body.includes('[x] **Documentation**')) labels.push('PR: Docs');
if (body.includes('contributing-guide: v1')) labels.push('follows-guidelines');
if (labels.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels,
});
}