Skip to content

check_membership.cjs error branch short-circuits before bot allowlist fallback #21098

@microsasa

Description

@microsasa

Description

When a GitHub App (e.g., Copilot) triggers a pull_request_review event, the context.actor is the App's login (e.g., Copilot). This actor is not a GitHub user, so getCollaboratorPermissionLevel() returns a 404.

In check_membership.cjs, this 404 is returned as {authorized: false, error: "Copilot is not a user"} from checkRepositoryPermission(). The calling code then hits the error branch and exits immediately — before ever checking the GH_AW_ALLOWED_BOTS fallback.

Impact

The bots: field in workflow frontmatter is effectively broken for any GitHub App whose context.actor is not a valid GitHub user. The bot is correctly listed in GH_AW_ALLOWED_BOTS in the compiled lock file, but check_membership.cjs never evaluates it.

Example: You want to build an autonomous review pipeline where Copilot reviews a PR, and a gh-aw agent responds to the review (e.g., addresses comments, approves, or merges). You configure:

on:
  pull_request_review:
    types: [submitted]
  bots: [Copilot]

This compiles correctly — GH_AW_ALLOWED_BOTS: Copilot appears in the lock file. But when Copilot submits a review:

  1. pre_activation runs check_membership.cjs
  2. Role check calls getCollaboratorPermissionLevel("Copilot") → 404 → {error: "Copilot is not a user"}
  3. The if (result.error) branch fires → sets is_team_member=false, result=api_errorreturns
  4. The bot allowlist check (isAllowedBot, checkBotStatus) is never reached
  5. activated output = falseactivation and agent jobs are skipped
  6. The agent never runs despite the bot being explicitly allowed

The workflow appears to work (no errors, pre_activation shows success) but silently does nothing. There is no log line indicating the bot check was attempted, making this difficult to debug.

Root Cause (in check_membership.cjs)

const result = await checkRepositoryPermission(actor, owner, repo, requiredPermissions);

if (result.error) {
  // EXIT HERE — never reaches bot check below
  core.setOutput("is_team_member", "false");
  core.setOutput("result", "api_error");
  return;
}

if (result.authorized) {
  // ...
} else {
  // Bot fallback lives here — unreachable when result.error is set
  if (allowedBots && allowedBots.length > 0) {
    if (isAllowedBot(actor, allowedBots)) {
      // ...
    }
  }
}

Repro Steps

  1. Create a gh-aw workflow triggered by pull_request_review
  2. Add a bot to the allowlist under on::
    on:
      pull_request_review:
        types: [submitted]
      bots: [Copilot]
  3. Compile with gh aw compile — lock file correctly shows GH_AW_ALLOWED_BOTS: Copilot
  4. Have Copilot submit a review on a PR
  5. Observe: pre_activation job succeeds, but activated output is falseactivation and agent jobs are skipped
  6. In pre_activation logs:
    GH_AW_ALLOWED_BOTS: Copilot
    Checking if user 'Copilot' has required permissions
    ⚠️ Repository permission check failed: Copilot is not a user
    
    No subsequent log line about checking the bots list.

Expected Behavior

When checkRepositoryPermission fails with an error for an actor that IS in GH_AW_ALLOWED_BOTS, the code should fall through to the bot allowlist check instead of exiting early.

Suggested Fix

Move the bot fallback check so it runs when result.error is set AND the actor is in the allowed bots list:

const result = await checkRepositoryPermission(actor, owner, repo, requiredPermissions);

if (result.authorized) {
  // ... authorized by role
} else {
  // Check bot fallback (whether error or insufficient permissions)
  if (allowedBots && allowedBots.length > 0 && isAllowedBot(actor, allowedBots)) {
    const botStatus = await checkBotStatus(actor, owner, repo);
    if (botStatus.isBot && botStatus.isActive) {
      // ... authorized as bot
      return;
    }
  }

  // Not authorized by role or bot
  if (result.error) {
    core.setOutput("result", "api_error");
  } else {
    core.setOutput("result", "insufficient_permissions");
  }
  core.setOutput("is_team_member", "false");
}

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions