diff --git a/src/githubUtils.ts b/src/githubUtils.ts index c80d07b..472345d 100644 --- a/src/githubUtils.ts +++ b/src/githubUtils.ts @@ -3,6 +3,24 @@ import { getApp } from './appUtils' import { AuthResponse, COMMAND_PREFIX, PRChanges, Task } from './constants' import { IssueComment, Milestone } from '@octokit/webhooks-types' +// Users allowed to request backports through PR comments. +// +// CONTRIBUTOR is intentionally included for users who have previously +// contributed commits to the repository, even if they do not currently +// have repository write access. Backport PRs still go thru normal +// approval/merge process once created. +const BACKPORT_REQUEST_ASSOCIATIONS = new Set([ + 'OWNER', + 'MEMBER', + 'COLLABORATOR', + 'CONTRIBUTOR', +]) + +export const isAllowedBackportAuthorAssociation = (authorAssociation?: string): boolean => { + return authorAssociation !== undefined && + BACKPORT_REQUEST_ASSOCIATIONS.has(authorAssociation) +} + export enum Reaction { THUMBS_UP = '+1', THUMBS_DOWN = '-1', @@ -218,8 +236,8 @@ export const getBackportRequestsFromPR = async (octokit: Octokit, task: Task): P .filter(comment => comment?.body?.trim().startsWith(COMMAND_PREFIX)) // Filter out forced requests .filter(comment => !comment?.body?.trim().startsWith(COMMAND_PREFIX + '!')) - // Filter out comments from non-collaborators - .filter(comment => comment?.author_association !== 'NONE') + // Filter out comments from users not authorized to request backports + .filter(comment => isAllowedBackportAuthorAssociation(comment?.author_association)) // Filter out comments that got rejected by the bot. // This is a safety measure, we will check the command again .filter(comment => comment.reactions?.confused === 0) as IssueComment[] diff --git a/src/index.ts b/src/index.ts index fd0567b..8b9b5e6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,7 @@ import { addToQueue } from './queue.js' import { ALLOWED_ORGS, CACHE_DIRNAME, COMMAND_PREFIX, LABEL_BACKPORT, PRIVATE_KEY_PATH, ROOT_DIR, SERVE_HOST, SERVE_PORT, TO_SEPARATOR, Task, WEBHOOK_SECRET, WORK_DIRNAME } from './constants.js' import { extractBranchFromPayload, extractCommitsFromPayload, isFriendly } from './payloadUtils.js' import { getApp } from './appUtils.js' -import { Reaction, addPRLabel, addReaction, getAuthToken, getBackportRequestsFromPR, getCommitsForPR, removePRLabel } from './githubUtils.js' +import { Reaction, addPRLabel, addReaction, getAuthToken, getBackportRequestsFromPR, getCommitsForPR, isAllowedBackportAuthorAssociation, removePRLabel } from './githubUtils.js' import { setGlobalGitConfig } from './gitUtils.js' const app = getApp() @@ -152,11 +152,11 @@ app.webhooks.on(['issue_comment.created'], async ({ payload }) => { // Check if the comment is a backport request if (body.trim().startsWith(COMMAND_PREFIX)) { - // Check if the author is at least a collaborator + // Check if the author is allowed to request a backport const commentAuthor = payload?.comment?.user.login const authorAssociation = payload?.comment?.author_association - if (!authorAssociation || authorAssociation === 'NONE') { - info(`Ignoring comment from non-collaborator: ${commentAuthor}`) + if (!isAllowedBackportAuthorAssociation(authorAssociation)) { + info(`Ignoring backport request from unauthorized user: ${commentAuthor}`) return }