You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Access denied: Potential confused deputy attack detected. Actor '<account_name>' does not match the event author. The workflow may have been triggered indirectly via a bot command. #39871
on:
pull_request:
types: [opened, reopened, synchronize]
We are noticing on events like: PR Release Notes #1: Pull request https://github.com/<org>/<repo>/pull/1 synchronize by <actor_name> the following error in pre-activation:
Access denied: Potential confused deputy attack detected. Actor '<actor_name>' does not match the event author. The workflow may have been triggered indirectly via a bot command.
The <actor_name> is matching 100% in both the event and error message.
The problem is that it is not accounted as error, but rather as warning in the pre-activation stage which is marked as success still. Nevertheless when trying to debug it further for the reason it turns out that if the original PR creator actor isn't matching with the actor of the last commits to PR. That means in practice it isn't fully about the last event actor, which means that collaboration on a single PR isn't possible with agentic workflow as such?
Triggered by @pelikhan — reviewing @maikelvdh's comment against the confused-deputy check
Executive Summary
@maikelvdh is correct. The pull_request:synchronize branch of the confused-deputy guard compares github.actor (whoever pushed the commit that triggered the sync) against pull_request.user.login (whoever opened the PR). Any mismatch is flagged — so when a second human collaborator pushes to a PR opened by someone else, the run is denied. The check is not bot-scoped, even though the ADR and code comments frame the attack purely in terms of bot actors. This blocks legitimate multi-author collaboration on a single PR.
if(eventName==="pull_request"&&payload.action==="synchronize"){constprAuthor=payload.pull_request?.user?.login;if(prAuthor!==undefined&&prAuthor!==actor){returntrue;// → denied in check_membership.cjs:194-202}}
actor = github.actor = the account whose push produced the synchronize event (the last-commit pusher).
prAuthor = the account that opened the PR.
The condition fires for anyactor !== prAuthor, with no [bot] restriction.
So the scenario in the issue — User A opens a PR, User B pushes a follow-up commit — yields actor=B, prAuthor=A, mismatch → result = confused_deputy → access denied. This matches @maikelvdh's observation exactly: "it isn't fully about the last event actor... collaboration on a single PR isn't possible."
Intent vs. implementation gap
The design clearly intended this to catch bot actors only:
The [Dependabot Confused Deputy attack]((labs.boostsecurity.io/redacted) works by making actor = dependabot[bot] to borrow the bot's elevated permissions against attacker-controlled code.
ADR-29450 Consequences (Negative rejig docs #1): "The pull_request:synchronize guard assumes that whenever a bot is the actor on a sync event, the bot should also be the PR author."
ADR Alternative 3 was rejected specifically to avoid blanket-denying bots — yet the shipped check blanket-denies all mismatched actors, humans included.
But the normative spec (ADR Part 2, item 2) and the implementation both drop the bot qualifier — they fire on a bare actor !== pull_request.user.login. That's the inconsistency producing the false positive.
Why it's a false positive for humans
A human collaborator pushing to another human's PR is not a confused-deputy situation: their own github.actor permissions are checked normally by checkRepositoryPermission immediately after this guard. There's no permission-borrowing — User B is exactly who they appear to be. The guard short-circuits before that legitimate permission check ever runs.
The test suite (check_permissions_utils.test.cjs:553-599) covers dependabot[bot] mismatches and matching-author cases, but has no test asserting the human-collaborator-pushes-to-anothers-PR case — which is precisely the uncovered behavior being reported.
Secondary point from the comment
@maikelvdh also noted it surfaces as a warning in pre_activation that still shows the job as success. That's accurate: check_membership.cjs:196 uses core.warning(...) (not core.setFailed), and signals denial via the result=confused_deputy output rather than a failed step — consistent with ADR §Membership-Check item 2/3.
Recommendations
Scope the synchronize check to bot actors to match the ADR's stated intent, e.g. fire only when actor.endsWith("[bot]") (mirroring the issue_comment branch's bot-aware handling) — or gate on the actor being a bot whose permissions would otherwise be borrowed.
Add a regression test: human actor ≠ human prAuthor on synchronize ⇒ false.
Reconcile ADR-29450 Part 2 item 2 with the narrative — the normative rule should carry the bot qualifier the narrative assumes, or the narrative should be corrected to say the guard is deliberately actor-agnostic.
Key sources
actions/setup/js/check_permissions_utils.cjs:92-106 (the helper)
actions/setup/js/check_membership.cjs:189-202 (the denial site)
Activity
pelikhan commentedon Jun 17, 2026
Could you ask the agentic-workflows agent to debug this issue deeper to determine what hapened?
maikelvdh commentedon Jun 18, 2026
The problem is that it is not accounted as error, but rather as warning in the pre-activation stage which is marked as success still. Nevertheless when trying to debug it further for the reason it turns out that if the original PR creator actor isn't matching with the actor of the last commits to PR. That means in practice it isn't fully about the last event actor, which means that collaboration on a single PR isn't possible with agentic workflow as such?
maikelvdh commentedon Jun 23, 2026
@pelikhan any feedback on the above? thanks!
pelikhan commentedon Jun 24, 2026
/scout review this comment against the confused deputed check
#39871 (comment)
github-actions commentedon Jun 24, 2026
🔭 Recon complete! Scout has charted the territory. Map ready! 🗺️
github-actions commentedon Jun 24, 2026
🔍 Scout Research Report
Triggered by
@pelikhan— reviewing @maikelvdh's comment against the confused-deputy checkExecutive Summary
@maikelvdh is correct. The
pull_request:synchronizebranch of the confused-deputy guard comparesgithub.actor(whoever pushed the commit that triggered the sync) againstpull_request.user.login(whoever opened the PR). Any mismatch is flagged — so when a second human collaborator pushes to a PR opened by someone else, the run is denied. The check is not bot-scoped, even though the ADR and code comments frame the attack purely in terms of bot actors. This blocks legitimate multi-author collaboration on a single PR.Click to expand detailed findings
Root cause
actions/setup/js/check_permissions_utils.cjs:101-106:actor=github.actor= the account whose push produced thesynchronizeevent (the last-commit pusher).prAuthor= the account that opened the PR.actor !== prAuthor, with no[bot]restriction.So the scenario in the issue — User A opens a PR, User B pushes a follow-up commit — yields
actor=B,prAuthor=A, mismatch →result = confused_deputy→ access denied. This matches @maikelvdh's observation exactly: "it isn't fully about the last event actor... collaboration on a single PR isn't possible."Intent vs. implementation gap
The design clearly intended this to catch bot actors only:
actor = dependabot[bot]to borrow the bot's elevated permissions against attacker-controlled code.pull_request:synchronizeguard assumes that whenever a bot is the actor on a sync event, the bot should also be the PR author."But the normative spec (ADR Part 2, item 2) and the implementation both drop the bot qualifier — they fire on a bare
actor !== pull_request.user.login. That's the inconsistency producing the false positive.Why it's a false positive for humans
A human collaborator pushing to another human's PR is not a confused-deputy situation: their own
github.actorpermissions are checked normally bycheckRepositoryPermissionimmediately after this guard. There's no permission-borrowing — User B is exactly who they appear to be. The guard short-circuits before that legitimate permission check ever runs.The test suite (
check_permissions_utils.test.cjs:553-599) coversdependabot[bot]mismatches and matching-author cases, but has no test asserting the human-collaborator-pushes-to-anothers-PR case — which is precisely the uncovered behavior being reported.Secondary point from the comment
@maikelvdh also noted it surfaces as a warning in
pre_activationthat still shows the job as success. That's accurate:check_membership.cjs:196usescore.warning(...)(notcore.setFailed), and signals denial via theresult=confused_deputyoutput rather than a failed step — consistent with ADR §Membership-Check item 2/3.Recommendations
synchronizecheck to bot actors to match the ADR's stated intent, e.g. fire only whenactor.endsWith("[bot]")(mirroring the issue_comment branch's bot-aware handling) — or gate on the actor being a bot whose permissions would otherwise be borrowed.actor≠ humanprAuthoronsynchronize⇒false.Key sources
actions/setup/js/check_permissions_utils.cjs:92-106(the helper)actions/setup/js/check_membership.cjs:189-202(the denial site)docs/adr/29450-guard-bot-filter-against-dependabot-confused-deputy-injection.mdSuggested next steps
isConfusedDeputyAttack+ add the human-collaborator regression test.maikelvdh commentedon Jun 25, 2026
How will this be handled further now? As scout analysis seems accurate.
cc: @pelikhan
maikelvdh commentedon Jul 3, 2026
@pelikhan is there anything which will happen with this issue to get it addressed?
fix: scope confused deputy check to bot actors on pull_request:synchr…