fix(actions): resolve pull_request_target reusable workflows at the base commit (#38886)

For a `pull_request_target` (PRT) run, Gitea loads the top-level
workflow from the trusted base branch, but any local reusable workflow
it calls (`uses: ./...`) was read from the PR **head** commit, which the
fork author controls.

## Fix

**Record the source commit where the content is read.**
`DetectedWorkflow` now carries a `SourceCommitSHA` filled in next to
`Content`, so the PRT detection pass at the base commit records the base
SHA automatically.

**Defense in depth.** `loadReusableWorkflowSource` pins the PR base
commit for a PRT run's local `uses: ./...` rather than trusting the
stored SHA. This also covers runs recorded before this change, whose
rows still hold the head SHA and would otherwise resolve from the fork
on rerun.

Existing run rows are not migrated.

---------

Co-authored-by: Zettat <zettat123@gmail.com>
This commit is contained in:
bircni
2026-08-12 19:03:44 +02:00
committed by GitHub
parent 2551f9949a
commit 8161479fde
8 changed files with 300 additions and 27 deletions
+5 -3
View File
@@ -62,6 +62,7 @@ func ParseScopedWorkflows(ctx context.Context, gitRepo *git.Repository, sourceCo
func MatchScopedWorkflows(
ctx context.Context,
parsed []*ParsedScopedWorkflow,
sourceCommitSHA string,
consumerGitRepo *git.Repository,
consumerCommit *git.Commit,
inputEvent webhook_module.HookEventType,
@@ -74,9 +75,10 @@ func MatchScopedWorkflows(
continue
}
dwf := &DetectedWorkflow{
EntryName: p.EntryName,
TriggerEvent: evt,
Content: p.Content,
EntryName: p.EntryName,
TriggerEvent: evt,
Content: p.Content,
SourceCommitSHA: sourceCommitSHA,
}
switch detectWorkflowMatch(ctx, consumerGitRepo, consumerCommit, inputEvent, payload, evt) {
case detectMatched:
+14 -9
View File
@@ -29,6 +29,8 @@ type DetectedWorkflow struct {
EntryName string
TriggerEvent *jobparser.Event
Content []byte
// SourceCommitSHA is the commit Content was read from, and must always be filled in together with Content.
SourceCommitSHA string
}
type detectResult int
@@ -205,17 +207,19 @@ func DetectWorkflows(
if evt.IsSchedule() {
if detectSchedule {
dwf := &DetectedWorkflow{
EntryName: entry.Name(),
TriggerEvent: evt,
Content: content,
EntryName: entry.Name(),
TriggerEvent: evt,
Content: content,
SourceCommitSHA: commit.ID.String(),
}
schedules = append(schedules, dwf)
}
} else {
dwf := &DetectedWorkflow{
EntryName: entry.Name(),
TriggerEvent: evt,
Content: content,
EntryName: entry.Name(),
TriggerEvent: evt,
Content: content,
SourceCommitSHA: commit.ID.String(),
}
switch detectWorkflowMatch(ctx, gitRepo, commit, triggedEvent, payload, evt) {
case detectMatched:
@@ -254,9 +258,10 @@ func DetectScheduledWorkflows(ctx context.Context, gitRepo *git.Repository, comm
if evt.IsSchedule() {
log.Trace("detect scheduled workflow: %q", entry.Name())
dwf := &DetectedWorkflow{
EntryName: entry.Name(),
TriggerEvent: evt,
Content: content,
EntryName: entry.Name(),
TriggerEvent: evt,
Content: content,
SourceCommitSHA: commit.ID.String(),
}
wfs = append(wfs, dwf)
}