mirror of
https://gitea.com/gitea/runner.git
synced 2026-11-13 11:23:42 +00:00
fix: support cache-mode and make low-trust runs cache read-only (#1412)
Low-trust runs like `pull_request_target` on the default branch could poison caches that `push` runs restore. As on GitHub, they now only restore unless `cache-mode` allows saving. This and the Gitea change can be deployed in any order. Refs: https://gitea.com/gitea/actionslib/pulls/33 https://github.com/go-gitea/gitea/pull/40343 _Written by Claude (Opus 5.5)._ Reviewed-on: https://gitea.com/gitea/runner/pulls/1412 Reviewed-by: bircni <[email protected]> Co-authored-by: silverwind <[email protected]>
This commit is contained in:
1 parent
c2046d597c
commit
4f59e3e418
8 files changed
+127
-26
No files matched your search
@@ -543,7 +543,14 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
|
||||
// is removed when the task finishes, so a leaked token stops working as
|
||||
// soon as the job ends rather than remaining valid for the runner's
|
||||
// lifetime.
|
||||
revokeCache, resultsURL := r.registerCacheForTask(giteaRuntimeToken, cacheCredential(preset, cacheURL), reporter)
|
||||
cred := cacheCredential(preset, cacheURL, cmp.Or(job.CacheMode, workflow.CacheMode))
|
||||
if cacheURL != "" {
|
||||
envs["ACTIONS_CACHE_MODE"] = cred.CacheMode // @actions/cache skips what the mode forbids
|
||||
if cred.CacheMode != "write" {
|
||||
reporter.Logf("Cache mode: %s", cred.CacheMode)
|
||||
}
|
||||
}
|
||||
revokeCache, resultsURL := r.registerCacheForTask(giteaRuntimeToken, cred, reporter)
|
||||
defer revokeCache()
|
||||
|
||||
eventJSON, err := json.Marshal(preset.Event)
|
||||
@@ -728,17 +735,24 @@ func (r *Runner) registerCacheForTask(token string, cred artifactcache.JobCreden
|
||||
}
|
||||
|
||||
// cacheCredential scopes the job's cache entries by ref, as GitHub does.
|
||||
func cacheCredential(preset *model.GithubContext, publicURL string) artifactcache.JobCredential {
|
||||
func cacheCredential(preset *model.GithubContext, publicURL, cacheMode string) artifactcache.JobCredential {
|
||||
ref := preset.Ref
|
||||
if strings.HasPrefix(preset.EventName, "pull_request") && preset.EventName != "pull_request_target" {
|
||||
number, _ := preset.Event["number"].(float64)
|
||||
if number, ok := preset.Event["number"].(float64); ok && strings.HasPrefix(preset.EventName, "pull_request") && preset.EventName != "pull_request_target" {
|
||||
ref = fmt.Sprintf("refs/pull/%d/head", int64(number)) // review events take github.ref from the reviewer's commit_id
|
||||
}
|
||||
defaultRef := branchRef(model.AsString(model.NestedMapLookup(preset.Event, "repository", "default_branch")))
|
||||
if cacheMode == "" { // only trusted events save caches for the default branch
|
||||
cacheMode = "write"
|
||||
if ref == defaultRef && !slices.Contains([]string{"push", "workflow_dispatch", "schedule", "delete", "registry_package"}, preset.EventName) {
|
||||
cacheMode = "read"
|
||||
}
|
||||
}
|
||||
return artifactcache.JobCredential{
|
||||
Repo: preset.Repository,
|
||||
Ref: ref,
|
||||
BaseRef: branchRef(preset.BaseRef),
|
||||
DefaultRef: branchRef(model.AsString(model.NestedMapLookup(preset.Event, "repository", "default_branch"))),
|
||||
DefaultRef: defaultRef,
|
||||
CacheMode: cacheMode,
|
||||
PublicURL: publicURL,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,15 +120,29 @@ func TestCacheCredential(t *testing.T) {
|
||||
Event: map[string]any{"repository": repository, "number": float64(3)},
|
||||
}
|
||||
assert.Equal(t, artifactcache.JobCredential{
|
||||
Repo: "owner/repo", Ref: "refs/pull/3/head", BaseRef: "refs/heads/release", DefaultRef: "refs/heads/main", PublicURL: "http://cache",
|
||||
}, cacheCredential(review, "http://cache"))
|
||||
Repo: "owner/repo", Ref: "refs/pull/3/head", BaseRef: "refs/heads/release", DefaultRef: "refs/heads/main", CacheMode: "write", PublicURL: "http://cache",
|
||||
}, cacheCredential(review, "http://cache", ""))
|
||||
|
||||
review.EventName, review.Ref = "pull_request_target", "refs/heads/release"
|
||||
assert.Equal(t, "refs/heads/release", cacheCredential(review, "").Ref)
|
||||
assert.Equal(t, "refs/heads/release", cacheCredential(review, "", "").Ref)
|
||||
assert.Equal(t, "write", cacheCredential(review, "", "").CacheMode)
|
||||
|
||||
assert.Equal(t, artifactcache.JobCredential{
|
||||
Repo: "owner/repo", Ref: "refs/heads/feature", DefaultRef: "refs/heads/main",
|
||||
}, cacheCredential(&model.GithubContext{Repository: "owner/repo", Ref: "refs/heads/feature", Event: map[string]any{"repository": repository}}, ""))
|
||||
Repo: "owner/repo", Ref: "refs/heads/feature", DefaultRef: "refs/heads/main", CacheMode: "write",
|
||||
}, cacheCredential(&model.GithubContext{Repository: "owner/repo", Ref: "refs/heads/feature", Event: map[string]any{"repository": repository}}, "", ""))
|
||||
|
||||
for _, tc := range [][3]string{
|
||||
{"pull_request_target", "", "read"},
|
||||
{"issue_comment", "", "read"},
|
||||
{"pull_request_comment", "", "read"},
|
||||
{"push", "", "write"},
|
||||
{"issue_comment", "write", "write"},
|
||||
{"push", "none", "none"},
|
||||
} {
|
||||
cred := cacheCredential(&model.GithubContext{EventName: tc[0], Ref: "refs/heads/main", Event: map[string]any{"repository": repository}}, "", tc[1])
|
||||
assert.Equal(t, "refs/heads/main", cred.Ref, tc)
|
||||
assert.Equal(t, tc[2], cred.CacheMode, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunner_ExternalCacheServer_RegisterRevoke(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user