-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Summary
The default workflow template in examples/claude.yml (also generated by /install-github-app) has no author_association check in its if: condition. On public repositories, any user who can open an issue or leave a comment containing @claude can trigger the workflow, which:
- Runs on the repo's GitHub Actions runner
- Accesses
secrets.ANTHROPIC_API_KEYorsecrets.CLAUDE_CODE_OAUTH_TOKEN - Requests an OIDC token (
id-token: write)
Current behavior
The if: condition only checks whether the event body contains @claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))Docs vs reality
The security docs state:
Repository Access: The action can only be triggered by users with write access to the repository
The action itself may perform permission checks internally, but the workflow job still starts — the runner spins up, checks out the repo, and the step receives the secret before the action can reject the user. This means:
- API keys / OAuth tokens are exposed to the runner environment regardless
- Runner minutes are consumed
- The OIDC token is available
Suggested fix
Add an author_association trust gate to the default template:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.review.author_association)) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.issue.author_association))This prevents the job from starting at all for unauthorized users, which is the only way to protect secrets from being passed to the runner.
Impact
Every public repository that uses the default template from examples/claude.yml or /install-github-app is affected.