anthropics / anthropics/claude-code-action

Default workflow template missing author trust gate — secrets exposed on public repos

Open Beginner friendly
#1,068 0 comments 0 reactions 0 assignees View on GitHub
area:permissions bug documentation p1
Dominant language
TypeScript
Stars
8.9k
Forks
2.1k
Avg merge
3d 9h
Merged PRs (30d)
10

Description

## Summary

The default workflow template in [`examples/claude.yml`](https://github.com/anthropics/claude-code-action/blob/main/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:

1. Runs on the repo's GitHub Actions runner
2. Accesses `secrets.ANTHROPIC_API_KEY` or `secrets.CLAUDE_CODE_OAUTH_TOKEN`
3. Requests an OIDC token (`id-token: write`)

## Current behavior

The `if:` condition only checks whether the event body contains `@claude`:

```yaml
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](https://github.com/anthropics/claude-code-action/blob/main/docs/security.md) 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:

```yaml
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.

Contributor guide

Open the contributing guide

Research direction

Start with examples/claude.yml and trace how /install-github-app generates or installs the default workflow. Review each event branch in the if: condition and add the specified author_association trust gate consistently. Done means unauthorized issue and comment authors cannot start the job, while trusted authors retain the existing @claude triggers.

Written by the indexing model from the issue text.

Assessment

Tech stack
github-actions
Domain
ci-cd, security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
62/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.