Skip to content

Commit 7535a7a

Browse files
revert: switch to public membership only approach
Remove fine-grained token implementation and revert to checking only public organization membership using default GITHUB_TOKEN: - Remove github_token input parameter from action - Remove token passing from workflow - Update logic to only detect public members (HTTP 200 + content) - Block private members and provide clear instructions to make membership public - Update documentation to explain public membership requirement - Add helpful messaging directing users to github.com/orgs/dotCMS/people This approach requires dotCMS team members to make their org membership public to access Claude workflows, but avoids token management complexity. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 00168aa commit 7535a7a

File tree

3 files changed

+41
-46
lines changed

3 files changed

+41
-46
lines changed

.github/actions/security/org-membership-check/README.md

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ This composite action checks if a GitHub user is a member of the dotCMS organiza
55
## Security Features
66

77
- **Hardcoded Organization**: The organization name "dotCMS" is hardcoded and cannot be overridden
8-
- **No Information Leakage**: Does not distinguish between public/private membership in outputs
8+
- **Public Membership Only**: Only detects public organization members for security
9+
- **Clear Instructions**: Provides guidance for private members to make membership public
910
- **Graceful Error Handling**: Returns clear status without exposing internal API details
1011

1112
## Inputs
1213

1314
| Input | Description | Required | Default |
1415
|-------|-------------|----------|---------|
1516
| `username` | GitHub username to check | Yes | N/A |
16-
| `github_token` | Fine-grained GitHub token with organization membership read permissions | Yes | N/A |
1717

1818
## Outputs
1919

@@ -30,7 +30,6 @@ This composite action checks if a GitHub user is a member of the dotCMS organiza
3030
uses: ./.github/actions/security/org-membership-check
3131
with:
3232
username: ${{ github.actor }}
33-
github_token: ${{ secrets.MACHINE_USER_CORE_ORG_MEMBERSHIP_CHECK }}
3433

3534
- name: Conditional step based on membership
3635
if: steps.membership-check.outputs.is_member == 'true'
@@ -41,23 +40,27 @@ This composite action checks if a GitHub user is a member of the dotCMS organiza
4140
4241
The action uses the GitHub CLI (`gh`) with the repository's `GITHUB_TOKEN` to check organization membership via the GitHub API endpoint `GET /orgs/dotCMS/members/{username}`.
4342

44-
**Key Design Decision: Status Code vs Response Body**
43+
**Important Limitation: Public Membership Only**
4544

46-
The action relies on HTTP status codes rather than parsing response content because:
45+
This approach only detects users with **public** organization membership:
4746

48-
- **HTTP 200 (Success)**: User is a member of the organization
49-
- Public members: API returns user object with populated fields
50-
- Private members: API returns empty response body (but still 200 OK)
47+
- **HTTP 200 + user object**: User is a PUBLIC member → **AUTHORIZED**
48+
- **HTTP 404**: User has private membership OR is not a member → **BLOCKED**
5149

52-
- **HTTP 404 (Not Found)**: User is not a member of the organization
53-
- Returns error object with "Not Found" message
50+
**For dotCMS Team Members with Private Membership:**
5451

55-
This approach correctly authorizes all organization members (including owners with private membership) without needing to handle different response formats or visibility settings.
52+
If you are a dotCMS organization member but have private membership visibility, you must make your membership public to access Claude workflows:
53+
54+
1. Visit: https://github.com/orgs/dotCMS/people
55+
2. Find your username in the list
56+
3. Click "Make public" next to your name
57+
58+
This ensures the security gate can detect your organization membership without requiring additional API tokens.
5659

5760
## Security Considerations
5861

5962
- Only checks membership in the dotCMS organization (hardcoded)
60-
- Does not expose whether membership is public or private
63+
- Only authorizes users with public organization membership
6164
- Logs authorization results without sensitive details
62-
- Uses fine-grained token with minimal required permissions (organization membership read)
63-
- Token should be regularly rotated and monitored for usage
65+
- Uses default GITHUB_TOKEN (no additional secrets required)
66+
- Provides clear instructions for private members to become public

.github/actions/security/org-membership-check/action.yml

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ inputs:
55
username:
66
description: 'GitHub username to check'
77
required: true
8-
github_token:
9-
description: 'Fine-grained GitHub token with organization membership read permissions'
10-
required: true
118

129
outputs:
1310
is_member:
@@ -26,44 +23,38 @@ runs:
2623
run: |
2724
echo "Checking organization membership for user: ${{ inputs.username }} in dotCMS organization"
2825
29-
# Use GitHub CLI to check organization membership
30-
# This uses a fine-grained token with organization membership read permissions
26+
# Use GitHub CLI to check PUBLIC organization membership
27+
# This uses the default GITHUB_TOKEN and only detects public members
3128
3229
set +e # Don't exit on error, we want to handle it gracefully
3330
34-
# Check organization membership using GitHub API
35-
#
36-
# IMPORTANT: We check the HTTP status code (via exit code) rather than parsing
37-
# the response body because GitHub's membership API has specific behavior:
31+
# Check PUBLIC organization membership using GitHub API
3832
#
39-
# HTTP 200 (exit code 0) = User IS a member (regardless of response content)
40-
# - Public member: Returns user object with populated fields
41-
# - Private member: Returns empty response body (but still 200 OK)
33+
# IMPORTANT: This approach only detects PUBLIC organization members.
34+
# Private members will appear as non-members and be blocked.
4235
#
43-
# HTTP 404 (exit code 1) = User is NOT a member
44-
# - Returns {"message":"Not Found",...} error response
36+
# API Behavior:
37+
# - HTTP 200 + user object = Public member (AUTHORIZED)
38+
# - HTTP 404 = Private member OR non-member (BLOCKED)
4539
#
46-
# This approach correctly handles both public and private organization members
47-
# without needing to distinguish between their visibility settings.
40+
# Team members with private membership must make their membership public
41+
# to access Claude workflows: github.com/orgs/dotCMS/people
4842
49-
response=$(gh api orgs/dotCMS/members/${{ inputs.username }} \
50-
--header "Authorization: token ${{ inputs.github_token }}" 2>/dev/null)
43+
echo "Checking PUBLIC organization membership for ${{ inputs.username }} in dotCMS..."
44+
45+
response=$(gh api orgs/dotCMS/members/${{ inputs.username }} 2>/dev/null)
5146
api_exit_code=$?
5247
53-
if [ $api_exit_code -eq 0 ]; then
54-
# HTTP 200: User is a member (public or private)
55-
# We check response content only for logging clarity, not authorization logic
56-
if [ -n "$response" ]; then
57-
echo "✅ User ${{ inputs.username }} is a member of dotCMS (public membership)"
58-
else
59-
echo "✅ User ${{ inputs.username }} is a member of dotCMS (private membership)"
60-
fi
48+
if [ $api_exit_code -eq 0 ] && [ -n "$response" ]; then
49+
# HTTP 200 with content: User is a PUBLIC member
50+
echo "✅ User ${{ inputs.username }} is a PUBLIC member of dotCMS"
6151
echo "is_member=true" >> $GITHUB_OUTPUT
6252
echo "membership_status=member" >> $GITHUB_OUTPUT
6353
else
64-
# HTTP 404 or other error: User is not a member
65-
66-
echo "❌ User ${{ inputs.username }} is not a member of dotCMS (API exit code: $api_exit_code)"
54+
# HTTP 404 or empty response: Private member or non-member
55+
echo "❌ User ${{ inputs.username }} is not a PUBLIC member of dotCMS"
56+
echo "⚠️ If you are a dotCMS team member with private membership, please make it public:"
57+
echo " Visit: https://github.com/orgs/dotCMS/people"
6758
echo "is_member=false" >> $GITHUB_OUTPUT
6859
echo "membership_status=non-member" >> $GITHUB_OUTPUT
6960
fi

.github/workflows/issue_comment_claude-code-review.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,15 @@ jobs:
3737
uses: ./.github/actions/security/org-membership-check
3838
with:
3939
username: ${{ github.event.comment.user.login || github.actor }}
40-
github_token: ${{ secrets.MACHINE_USER_CORE_ORG_MEMBERSHIP_CHECK }}
4140

4241
- name: Log security decision
4342
run: |
4443
if [ "${{ steps.membership-check.outputs.is_member }}" = "true" ]; then
45-
echo "✅ Access granted: User is a dotCMS organization member"
44+
echo "✅ Access granted: User is a PUBLIC dotCMS organization member"
4645
else
47-
echo "❌ Access denied: User is not a dotCMS organization member"
46+
echo "❌ Access denied: User is not a PUBLIC dotCMS organization member"
47+
echo "⚠️ If you are a dotCMS team member, please make your membership public:"
48+
echo " Visit: https://github.com/orgs/dotCMS/people"
4849
echo "::warning::Unauthorized user attempted to trigger Claude workflow: ${{ github.event.comment.user.login || github.actor }}"
4950
fi
5051

0 commit comments

Comments
 (0)