Skip to content
GitHubGHSA-r8gh-v7wv-8g7h

GitHub: improper authorization

High8.8Published Aug 16, 2026

### Summary `isConfusedDeputyAttack` exists to catch the case where the actor who triggered a workflow is not the author of the content the agent will read. It handles four event names. `pull_request_target` is not one of them, and the string does not appear anywhere in the file. On `pull_request_target: types: [labeled]` the actor is the maintainer who applied the label. The membership gate validates that maintainer, the confused-deputy check returns false because it has no branch for the event, and the agent then runs in the base-repo context reading a fork PR written by someone else entirely. The gate answers "may this person start the agent". Nobody asks "who wrote the content the agent is about to read". I want to put the argument plainly, because the easy reply is that the adopter chose an untrusted trigger. I am not arguing that actor != author is always suspicious. For `pull_request` your own comment gives a good reason to tolerate it, and I agree with it. The argument is narrower: this mechanism exists specifically to detect the confused-deputy condition, and it omits the event where the actor/content principal mismatch intersects with the highest-trust execution cont...

GitHub advisory

Affected versions

PackageAffectedFixed in
github/gh-aw
Product
< v0.87.0v0.87.0
Details and references

### Summary `isConfusedDeputyAttack` exists to catch the case where the actor who triggered a workflow is not the author of the content the agent will read. It handles four event names. `pull_request_target` is not one of them, and the string does not appear anywhere in the file. On `pull_request_target: types: [labeled]` the actor is the maintainer who applied the label. The membership gate validates that maintainer, the confused-deputy check returns false because it has no branch for the event, and the agent then runs in the base-repo context reading a fork PR written by someone else entirely. The gate answers "may this person start the agent". Nobody asks "who wrote the content the agent is about to read". I want to put the argument plainly, because the easy reply is that the adopter chose an untrusted trigger. I am not arguing that actor != author is always suspicious. For `pull_request` your own comment gives a good reason to tolerate it, and I agree with it. The argument is narrower: this mechanism exists specifically to detect the confused-deputy condition, and it omits the event where the actor/content principal mismatch intersects with the highest-trust execution context. The check covers four sibling events and skips that one. An adopter using a documented trigger with a documented `forks:` key gets no coverage from a function written for exactly their situation. The distinction that matters for a fix is fork versus internal, not labeled versus not. A PR where `pull_request.head.repo.full_name != pull_request.base.repo.full_name` is authored by someone outside the repository's trust boundary; a same-repo PR is not, and its author already holds repository permissions. Keying on that boundary lets you close this without disturbing the ordinary case of a maintainer labelling an internal PR, which I expect is the false-positive you would worry about first. ### Details `actions/setup/js/check_permissions_utils.cjs`, `isConfusedDeputyAttack` at `:107`. The branches are: - `:120` `pull_request` — only when `action === "synchronize"` and the actor ends with `[bot]` - `:130` `pull_request_review` — reviewer must match the actor - `:140` `pull_request_review_comment` — comment author must match the actor - `:161` `issue_comment` — comment author must match the actor Then `return false`. `grep -c pull_request_target` on that file returns **0** at v0.85.4 and **0** on main at `b2ef1f3`. The comment at `:114-116` explains the design for `pull_request`: > Other pull_request actions (labeled, unlabeled, assigned, review_requested, > etc.) legitimately have actor != pr_author — the actor is whoever performed the > action, not the PR author — so checking those would cause false positives. That reasoning is sound for `pull_request`, which does not get repository secrets on a fork PR. `pull_request_target` does. The same "actor != author is normal" assumption is carried into an event where the content is untrusted and the token is not, and the file never distinguishes them because it never sees the event name at all. `check_membership.cjs:195` calls the function and, when it returns false, falls through to `checkRepositoryPermission` on `context.actor` — the labeler. ### PoC 7 assertions driving the unmodified shipped `check_permissions_utils.cjs`. No network, no third-party system. Attached, runs in one command. Environment: node 18.19.1, WSL2 Ubuntu 24.04. The payload is a `labeled` event where the PR author is `attacker` and the actor is `maintainer`: - **T1** At v0.85.4, `isConfusedDeputyAttack("maintainer", "pull_request_target", …)` returns **false**. The attacker-authored PR is not flagged. - **T2** Positive controls: the same harness gets **true** for `issue_comment` and `pull_request_review` with the same author/actor mismatch. The branches that exist do fire, so a false negative in T1 is a real gap and not a broken t

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Severity from
GitHub (reviewed advisory)
Weakness
CWE-441, CWE-863

More GitHub advisories

All GitHub

Critical advisories by email

Wednesdays: the week’s critical and high advisories in the AI and data stack, with the fixed versions. Only in weeks that have some.

Double opt-in. Unsubscribe any time.