mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-08-22 11:28:55 +08:00
Check collaborator permissions for workflow_run events (#1590)
The write-permission gate previously only ran for issue/PR entity events. Apply it to workflow_run events as well, checking both the workflow actor and the actor recorded on the upstream run when they differ. allowed_non_write_users and the github_token override behave the same as for entity events. Document the behavior for workflow_run pipelines.
This commit is contained in:
@@ -9,7 +9,11 @@ import * as core from "@actions/core";
|
||||
import { setupGitHubToken } from "../github/token";
|
||||
import { checkWritePermissions } from "../github/validation/permissions";
|
||||
import { createOctokit } from "../github/api/client";
|
||||
import { parseGitHubContext, isEntityContext } from "../github/context";
|
||||
import {
|
||||
parseGitHubContext,
|
||||
isEntityContext,
|
||||
isWorkflowRunEvent,
|
||||
} from "../github/context";
|
||||
import { detectMode } from "../modes/detector";
|
||||
import { prepareTagMode } from "../modes/tag";
|
||||
import { prepareAgentMode } from "../modes/agent";
|
||||
@@ -33,8 +37,8 @@ async function run() {
|
||||
const githubToken = await setupGitHubToken();
|
||||
const octokit = createOctokit(githubToken);
|
||||
|
||||
// Step 3: Check write permissions (only for entity contexts)
|
||||
if (isEntityContext(context)) {
|
||||
// Step 3: Check write permissions (entity contexts and workflow_run)
|
||||
if (isEntityContext(context) || isWorkflowRunEvent(context)) {
|
||||
// Check if github_token was provided as input (not from app)
|
||||
const githubTokenProvided = !!process.env.OVERRIDE_GITHUB_TOKEN;
|
||||
const hasWritePermissions = await checkWritePermissions(
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
isPullRequestEvent,
|
||||
isPullRequestReviewEvent,
|
||||
isPullRequestReviewCommentEvent,
|
||||
isWorkflowRunEvent,
|
||||
} from "../github/context";
|
||||
import type { GitHubContext } from "../github/context";
|
||||
import { detectMode } from "../modes/detector";
|
||||
@@ -185,8 +186,10 @@ async function run() {
|
||||
process.env.GITHUB_TOKEN = githubToken;
|
||||
process.env.GH_TOKEN = githubToken;
|
||||
|
||||
// Check write permissions (only for entity contexts)
|
||||
if (isEntityContext(context)) {
|
||||
// Check write permissions for entity contexts, and for workflow_run
|
||||
// events, whose upstream run may have been started by an actor without
|
||||
// write access (e.g. the author of a fork pull request)
|
||||
if (isEntityContext(context) || isWorkflowRunEvent(context)) {
|
||||
const hasWritePermissions = await checkWritePermissions(
|
||||
octokit.rest,
|
||||
context,
|
||||
|
||||
@@ -282,6 +282,12 @@ export function isPullRequestReviewCommentEvent(
|
||||
return context.eventName === "pull_request_review_comment";
|
||||
}
|
||||
|
||||
export function isWorkflowRunEvent(
|
||||
context: GitHubContext,
|
||||
): context is AutomationContext & { payload: WorkflowRunEvent } {
|
||||
return context.eventName === "workflow_run";
|
||||
}
|
||||
|
||||
export function isIssuesAssignedEvent(
|
||||
context: GitHubContext,
|
||||
): context is ParsedGitHubContext & { payload: IssuesAssignedEvent } {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as core from "@actions/core";
|
||||
import type { ParsedGitHubContext } from "../context";
|
||||
import { isWorkflowRunEvent, type GitHubContext } from "../context";
|
||||
import type { Octokit } from "@octokit/rest";
|
||||
|
||||
/**
|
||||
@@ -24,6 +24,28 @@ function isAllowedBot(actor: string, allowedBots: string): boolean {
|
||||
return allowedList.includes(normalizedActor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the actors whose repository access should be checked. This is
|
||||
* normally just the workflow actor (GITHUB_ACTOR). For workflow_run events
|
||||
* the actor that started the upstream run is checked as well when it
|
||||
* differs, since that is the account the run originates from.
|
||||
*/
|
||||
function getActorsToCheck(context: GitHubContext): string[] {
|
||||
const actors = [context.actor];
|
||||
|
||||
if (isWorkflowRunEvent(context)) {
|
||||
const runActor = context.payload.workflow_run?.actor?.login;
|
||||
if (runActor && !actors.includes(runActor)) {
|
||||
core.info(
|
||||
`workflow_run was started by ${runActor}; checking permissions for that actor as well`,
|
||||
);
|
||||
actors.push(runActor);
|
||||
}
|
||||
}
|
||||
|
||||
return actors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the actor has write permissions to the repository
|
||||
* @param octokit - The Octokit REST client
|
||||
@@ -34,11 +56,31 @@ function isAllowedBot(actor: string, allowedBots: string): boolean {
|
||||
*/
|
||||
export async function checkWritePermissions(
|
||||
octokit: Octokit,
|
||||
context: ParsedGitHubContext,
|
||||
context: GitHubContext,
|
||||
allowedNonWriteUsers?: string,
|
||||
githubTokenProvided?: boolean,
|
||||
): Promise<boolean> {
|
||||
const { repository, actor } = context;
|
||||
for (const actor of getActorsToCheck(context)) {
|
||||
const allowed = await checkActorWritePermissions(
|
||||
octokit,
|
||||
context,
|
||||
actor,
|
||||
allowedNonWriteUsers,
|
||||
githubTokenProvided,
|
||||
);
|
||||
if (!allowed) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function checkActorWritePermissions(
|
||||
octokit: Octokit,
|
||||
context: GitHubContext,
|
||||
actor: string,
|
||||
allowedNonWriteUsers?: string,
|
||||
githubTokenProvided?: boolean,
|
||||
): Promise<boolean> {
|
||||
const { repository } = context;
|
||||
const allowedBots = context.inputs.allowedBots ?? "";
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user