fix(actions): enforce fork pull request trust boundaries (#39005) (#39018)

Backport #39005 by @bircni

Preserve fork pull request restrictions across review-triggered
workflows, reusable workflow access, job scheduling, and filtered
workflow statuses.

This prevents untrusted fork workflow content from bypassing approval,
accessing private reusable workflows, or satisfying protected status
checks.


_Assisted-by: Codex:GPT-5_

Co-authored-by: bircni <bircni@icloud.com>
This commit is contained in:
Giteabot
2026-08-21 07:25:59 -07:00
committed by GitHub
parent bf7b6f8bd4
commit dbc17db0c2
7 changed files with 85 additions and 3 deletions
@@ -122,6 +122,18 @@ func TestGetActionsUserRepoPermission(t *testing.T) {
require.NoError(t, err)
assert.False(t, perm.CanRead(unit.TypeCode))
// Reusable workflows use a separate authorization path and must enforce
// the same fork-PR restriction.
run := &actions_model.ActionRun{RepoID: repo2.ID, IsForkPullRequest: true}
allowed, err := CanReadWorkflowCrossRepo(ctx, repo15, run)
require.NoError(t, err)
assert.False(t, allowed)
run.IsForkPullRequest = false
allowed, err = CanReadWorkflowCrossRepo(ctx, repo15, run)
require.NoError(t, err)
assert.True(t, allowed)
// Restore state for subsequent subtests.
task53.IsForkPullRequest = false
require.NoError(t, actions_model.UpdateTask(ctx, task53, "is_fork_pull_request"))
+1 -1
View File
@@ -675,7 +675,7 @@ func CanReadWorkflowCrossRepo(ctx context.Context, targetRepo *repo_model.Reposi
// logs in a publicly visible run; requiring a private caller keeps private content flowing private -> private.
// This is intentionally stricter than GitHub, which gates on the target repo's access setting (introduced in #32562):
// https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#allowing-access-to-components-in-a-private-repository
if run.Repo.IsPrivate {
if run.Repo.IsPrivate && !run.IsForkPullRequest {
if actionsUnit, err := targetRepo.GetUnit(ctx, unit.TypeActions); err == nil {
if actionsUnit.ActionsConfig().IsCollaborativeOwner(run.Repo.OwnerID) {
return true, nil