fix(ci): use getCollaboratorPermissionLevel for trust check (#26306)
* fix(ci): use GitHub App token for team membership check The default GITHUB_TOKEN doesn't have org-level permissions to query team membership via the teams.getMembershipForUserInOrg API. This causes the API to return 404 even for valid team members, making the trust check incorrectly fail for core team members. This change generates a short-lived GitHub App token with org-level access to properly verify team membership. Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix(ci): use getCollaboratorPermissionLevel for trust check Replace the team membership check (teams.getMembershipForUserInOrg) with getCollaboratorPermissionLevel which works with the default GITHUB_TOKEN. This approach: - Checks if users have write/maintain/admin access to the repo - Works without requiring a GitHub App token with org-level permissions - Is semantically correct: if someone can push to the repo, they're trusted The previous approach using teams.getMembershipForUserInOrg required org-level permissions that the default GITHUB_TOKEN doesn't have, causing 404 errors even for valid team members. Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * Apply suggestion from @keithwillcode --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
ae14e10b24
commit
e4753778fe
1 file changed
+19
-18
+19
-18
@@ -57,18 +57,19 @@ jobs:
|
||||
const prNumber = pr.number;
|
||||
const headSha = pr.head.sha;
|
||||
|
||||
async function isCoreTeamMember(username) {
|
||||
// Check if user has write access or higher (admin, maintain, write)
|
||||
async function hasWriteAccess(username) {
|
||||
try {
|
||||
await github.rest.teams.getMembershipForUserInOrg({
|
||||
org: owner,
|
||||
team_slug: 'core',
|
||||
const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner,
|
||||
repo,
|
||||
username,
|
||||
});
|
||||
return true;
|
||||
const hasAccess = ['admin', 'maintain', 'write'].includes(permission.permission);
|
||||
console.log(`User ${username}: hasWriteAccess=${hasAccess}`);
|
||||
return hasAccess;
|
||||
} catch (e) {
|
||||
if (e.status !== 404) {
|
||||
console.log(`Could not verify core team membership for ${username}: ${e.message}`);
|
||||
}
|
||||
console.log(`Could not check permission for ${username}: ${e.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -85,16 +86,16 @@ jobs:
|
||||
return;
|
||||
}
|
||||
|
||||
// Check 2: Verify core team membership via API (author_association can be unreliable)
|
||||
if (await isCoreTeamMember(pr.user.login)) {
|
||||
console.log(`Author ${pr.user.login} verified as core team member`);
|
||||
// Check 2: Verify write access via API (author_association can be unreliable)
|
||||
if (await hasWriteAccess(pr.user.login)) {
|
||||
console.log(`Author ${pr.user.login} verified as having write access`);
|
||||
core.setOutput('is-trusted', true);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Author ${pr.user.login} is not a core team member, checking for approval...`);
|
||||
console.log(`Author ${pr.user.login} does not have write access, checking for approval...`);
|
||||
|
||||
// Check 3: Has a core team member approved the current commit?
|
||||
// Check 3: Has someone with write access approved the current commit?
|
||||
const reviews = await github.paginate(github.rest.pulls.listReviews, {
|
||||
owner,
|
||||
repo,
|
||||
@@ -128,21 +129,21 @@ jobs:
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any approver is a core team member
|
||||
// Check if any approver has write access
|
||||
for (const [reviewer, reviewData] of reviewerStates) {
|
||||
if (reviewData.state !== 'APPROVED') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (await isCoreTeamMember(reviewer)) {
|
||||
console.log(`PR approved by core team member ${reviewer} for commit ${headSha}`);
|
||||
if (await hasWriteAccess(reviewer)) {
|
||||
console.log(`PR approved by ${reviewer} (has write access) for commit ${headSha}`);
|
||||
core.setOutput('is-trusted', true);
|
||||
return;
|
||||
}
|
||||
console.log(`Reviewer ${reviewer} is not a core team member`);
|
||||
console.log(`Reviewer ${reviewer} does not have write access`);
|
||||
}
|
||||
|
||||
console.log('PR requires approval from a core team member before CI can run');
|
||||
console.log('PR requires approval from someone with write access before CI can run');
|
||||
core.setOutput('is-trusted', false);
|
||||
|
||||
prepare:
|
||||
|
||||
Reference in new issue
Block a user