Skip to content

Commit 19a08fd

Browse files
fix: Issue 33050 claude workflow security (#420)
fixes false negative for private members --------- Co-authored-by: Claude <[email protected]>
1 parent b48032d commit 19a08fd

File tree

2 files changed

+42
-24
lines changed

2 files changed

+42
-24
lines changed

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,20 @@ This composite action checks if a GitHub user is a member of the dotCMS organiza
3737
3838
## Implementation Details
3939
40-
The action uses the GitHub CLI (`gh`) with the repository's `GITHUB_TOKEN` to check organization membership. It first attempts to check public membership, and if that fails, it attempts to check private membership (which requires appropriate permissions in organization repositories).
40+
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}`.
41+
42+
**Key Design Decision: Status Code vs Response Body**
43+
44+
The action relies on HTTP status codes rather than parsing response content because:
45+
46+
- **HTTP 200 (Success)**: User is a member of the organization
47+
- Public members: API returns user object with populated fields
48+
- Private members: API returns empty response body (but still 200 OK)
49+
50+
- **HTTP 404 (Not Found)**: User is not a member of the organization
51+
- Returns error object with "Not Found" message
52+
53+
This approach correctly authorizes all organization members (including owners with private membership) without needing to handle different response formats or visibility settings.
4154

4255
## Security Considerations
4356

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

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,34 +28,39 @@ runs:
2828
2929
set +e # Don't exit on error, we want to handle it gracefully
3030
31-
# Check public membership first
32-
gh api orgs/dotCMS/members/${{ inputs.username }} \
33-
--jq '.login' 2>/dev/null
31+
# Check organization membership using GitHub API
32+
#
33+
# IMPORTANT: We check the HTTP status code (via exit code) rather than parsing
34+
# the response body because GitHub's membership API has specific behavior:
35+
#
36+
# HTTP 200 (exit code 0) = User IS a member (regardless of response content)
37+
# - Public member: Returns user object with populated fields
38+
# - Private member: Returns empty response body (but still 200 OK)
39+
#
40+
# HTTP 404 (exit code 1) = User is NOT a member
41+
# - Returns {"message":"Not Found",...} error response
42+
#
43+
# This approach correctly handles both public and private organization members
44+
# without needing to distinguish between their visibility settings.
3445
35-
exit_code=$?
46+
response=$(gh api orgs/dotCMS/members/${{ inputs.username }} 2>/dev/null)
47+
api_exit_code=$?
3648
37-
if [ $exit_code -eq 0 ]; then
38-
echo "✅ User ${{ inputs.username }} is a member of dotCMS"
49+
if [ $api_exit_code -eq 0 ]; then
50+
# HTTP 200: User is a member (public or private)
51+
# We check response content only for logging clarity, not authorization logic
52+
if [ -n "$response" ]; then
53+
echo "✅ User ${{ inputs.username }} is a member of dotCMS (public membership)"
54+
else
55+
echo "✅ User ${{ inputs.username }} is a member of dotCMS (private membership)"
56+
fi
3957
echo "is_member=true" >> $GITHUB_OUTPUT
4058
echo "membership_status=member" >> $GITHUB_OUTPUT
4159
else
42-
# If public membership check fails, try to check private membership
43-
# This requires higher permissions but will work in organization repos
44-
gh api orgs/dotCMS/members/${{ inputs.username }} \
45-
--method GET \
46-
--header "Accept: application/vnd.github.v3+json" 2>/dev/null
47-
48-
private_exit_code=$?
49-
50-
if [ $private_exit_code -eq 0 ]; then
51-
echo "✅ User ${{ inputs.username }} is a member of dotCMS"
52-
echo "is_member=true" >> $GITHUB_OUTPUT
53-
echo "membership_status=member" >> $GITHUB_OUTPUT
54-
else
55-
echo "❌ User ${{ inputs.username }} is not a member of dotCMS"
56-
echo "is_member=false" >> $GITHUB_OUTPUT
57-
echo "membership_status=non-member" >> $GITHUB_OUTPUT
58-
fi
60+
# HTTP 404 or other error: User is not a member
61+
echo "❌ User ${{ inputs.username }} is not a member of dotCMS (API exit code: $api_exit_code)"
62+
echo "is_member=false" >> $GITHUB_OUTPUT
63+
echo "membership_status=non-member" >> $GITHUB_OUTPUT
5964
fi
6065
6166
# Log the result for debugging (without leaking membership details)

0 commit comments

Comments
 (0)