-
Notifications
You must be signed in to change notification settings - Fork 1.7k
allowed_bots bypasses actor check but not permission check — bot actors still fail with 404 #1133
Description
Summary
allowed_bots bypasses the human actor check (checkHumanActor in actor.ts) but does not bypass the write permission check (checkWritePermissions in permissions.ts). When a bot like GitHub Copilot triggers a pull_request_review event, the action fails with a 404 from the collaborators API because bot accounts are not repository collaborators.
Reproduction
- Configure a workflow triggered by
pull_request_reviewwithallowed_bots: "Copilot" - Request a Copilot review on a PR
- Copilot submits its review, triggering the workflow with
actor: Copilot
Expected: The action runs successfully, with allowed_bots bypassing both the actor check and the permission check.
Actual: The action fails:
Checking permissions for actor: Copilot
GET /repos/{owner}/{repo}/collaborators/Copilot/permission - 404
##[error] Failed to check permissions: HttpError: Copilot is not a user
##[error] Action failed with error: Actor does not have write permissions to the repository
Root cause
In src/github/validation/permissions.ts, checkWritePermissions has three bypass paths:
allowedNonWriteUsersmatch — but this uses a separate input (allowed_non_write_users), notallowed_botsactor.endsWith("[bot]")— but Copilot's login isCopilot, notcopilot-pull-request-reviewer[bot]- Collaborators API returns
adminorwrite— but bot accounts are not collaborators, so the API returns 404
None of these paths handle a bot that is listed in allowed_bots but doesn't have the [bot] suffix.
Meanwhile, checkHumanActor in actor.ts correctly handles allowed_bots — it fetches the user type via octokit.users.getByUsername, sees type: "Bot", and checks the allowed_bots list. But execution never reaches that point because checkWritePermissions throws first.
The call order in src/entrypoints/run.ts (line ~175):
// checkWritePermissions runs first and throws for bot actors
const hasWritePermissions = await checkWritePermissions(
octokit.rest, context, context.inputs.allowedNonWriteUsers, ...
);
// checkHumanActor runs later (in prepareAgentMode) and correctly checks allowed_bots
await checkHumanActor(octokit.rest, context);Suggested fix
In checkWritePermissions, add an allowed_bots check that mirrors the logic in checkHumanActor. If the actor is listed in allowed_bots (or allowed_bots is *), bypass the permission check. This could be done by:
- Passing
allowedBotsintocheckWritePermissions - Catching the 404 from the collaborators API and checking
allowed_botsbefore throwing - Or moving the
allowed_botscheck earlier inrun.tsbefore the permission check
Context
- GitHub Copilot's review actor login is
Copilot(type:Bot, id:175728472), notcopilot-pull-request-reviewer[bot] - This was discovered while building a Copilot review triage workflow that uses Claude to evaluate and respond to Copilot's review comments
- The
allowed_botsinput description says it allows "bot usernames" to trigger the action, which implies it should handle the full lifecycle including permission checks - Version tested: v1.0.77 (pinned SHA
ff9acae5886d41a99ed4ec14b7dc147d55834722)
Workaround
Re-dispatch the workflow via workflow_dispatch so the triggering actor becomes a human user instead of the bot. This works but adds complexity and latency.