Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ jobs:
if: |
(
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') &&
(github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR')) ||
(github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR' ||
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@claude
is this safe chage to give permission internal contributors to trigger claude github actions?

Copy link
Copy Markdown

@github-actions github-actions bot Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude finished @piatoss3612's task in 1m 14s —— View job


Security Analysis

TL;DR: No, this change is NOT safe. It introduces a critical security vulnerability that allows any GitHub user to trigger the Claude workflow with your repository's secrets and permissions.


Critical Security Vulnerability

The current implementation has a fundamental flaw: it checks WHERE the PR came from, not WHO is commenting. This creates an authorization bypass that allows any GitHub user to trigger Claude on internal PRs.

Attack Scenario

  1. An organization member creates a PR from a branch in chainapsis/keplr-example (not a fork)
  2. Any random GitHub user comments @claude on that PR
  3. The workflow triggers because github.event.pull_request.head.repo.full_name == github.repository is true
  4. The author_association check is completely bypassed via the OR condition
  5. The attacker now has Claude executing with your repository secrets and write permissions

Why This Happens

The logic uses OR conditions:

(author is OWNER/MEMBER/COLLABORATOR) OR (PR is from same repo)

This means if EITHER condition is true, the workflow runs. The second condition doesn't care who's commenting.


Additional Technical Issue

For issue_comment events (line 19), github.event.pull_request doesn't exist in the event payload. You're checking github.event.pull_request.head.repo.full_name, but this object is only available for pull_request, pull_request_review, and pull_request_review_comment events.

For issue_comment on a PR, you need to:

  • Use github.event.issue.pull_request.url to detect if it's a PR
  • Fetch PR details via GitHub API to get the head repo information

Reference: GitHub issue_comment event payload docs


Recommended Fix

Use AND logic to require BOTH a valid association AND same-repo for CONTRIBUTOR level:

if: |
  (
    (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') &&
     (github.event.comment.author_association == 'OWNER' || 
      github.event.comment.author_association == 'MEMBER' || 
      github.event.comment.author_association == 'COLLABORATOR' ||
      (github.event.comment.author_association == 'CONTRIBUTOR' && 
       github.event.issue.pull_request != null && 
       /* fetch PR via API to check head repo */))) ||
    (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') &&
     (github.event.comment.author_association == 'OWNER' || 
      github.event.comment.author_association == 'MEMBER' || 
      github.event.comment.author_association == 'COLLABORATOR' ||
      (github.event.comment.author_association == 'CONTRIBUTOR' && 
       github.event.pull_request.head.repo.full_name == github.repository))) ||
    (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') &&
     (github.event.review.author_association == 'OWNER' || 
      github.event.review.author_association == 'MEMBER' || 
      github.event.review.author_association == 'COLLABORATOR' ||
      (github.event.review.author_association == 'CONTRIBUTOR' && 
       github.event.pull_request.head.repo.full_name == github.repository))) ||
    (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) &&
     (github.event.issue.author_association == 'OWNER' || 
      github.event.issue.author_association == 'MEMBER' || 
      github.event.issue.author_association == 'COLLABORATOR'))
  )

This ensures:

  • OWNER/MEMBER/COLLABORATOR can trigger from anywhere (existing behavior)
  • CONTRIBUTOR can only trigger on non-fork PRs (your goal)
  • Random users cannot trigger on any PRs (closes the vulnerability)

Alternative Approach

If private organization members truly appear as CONTRIBUTOR, consider:

  1. Grant them MEMBER status in the organization (preferred)
  2. Use a GitHub App with more granular permissions
  3. Add specific users to an allowlist in the workflow

My Assessment

Do not merge this PR. The security risk outweighs the convenience. I recommend addressing the root cause (organization member permissions) rather than loosening the security model in a way that creates vulnerabilities.

(github.event.issue.pull_request && github.event.pull_request.head.repo.full_name == github.repository))) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') &&
(github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR')) ||
(github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR' ||
github.event.pull_request.head.repo.full_name == github.repository)) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') &&
(github.event.review.author_association == 'OWNER' || github.event.review.author_association == 'MEMBER' || github.event.review.author_association == 'COLLABORATOR')) ||
(github.event.review.author_association == 'OWNER' || github.event.review.author_association == 'MEMBER' || github.event.review.author_association == 'COLLABORATOR' ||
github.event.pull_request.head.repo.full_name == github.repository)) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) &&
(github.event.issue.author_association == 'OWNER' || github.event.issue.author_association == 'MEMBER' || github.event.issue.author_association == 'COLLABORATOR'))
)
Expand Down