From 11a96bb46274b2cadda71f5d4c901e6e111e1dbf Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Thu, 28 Aug 2025 10:17:00 +0200 Subject: [PATCH] fix(security): ensure unique names for container images created by actions Container images built by the runner are tagged with a unique name: - based on the specified `uses` URL for remote actions. - random for local actions. In the case of local actions, this will create new tags for each run but the images (and their layers) will be shared and not be duplicated. The least recently used tags can be garbage collected by tools such as https://github.com/stepchowfun/docuum. Using a different method for creating the tag name for the remote actions is to help with maintenance by establishing a direct relation with the `uses` field. It was instead relying on a name transformed multiple times which makes it more difficult to verify name collision are not accidentally made possible by one of those transformations. Without this fix, when a workflow ran a local [docker action](https://forgejo.org/docs/next/user/actions/actions/#docker-actions) (e.g. the [example in the end-to-end tests](https://code.forgejo.org/forgejo/end-to-end/src/commit/8f920b4b7adcb920e435db1acd02407b2312ab74/actions/example-force-rebuild/.forgejo/workflows/test.yml)), it used an image tag that could collide with other workflows that happen to use the same name. The workaround for older runner versions is to set [`[container].force_rebuild: true`](https://forgejo.org/docs/next/admin/actions/runner-installation/#configuration) in the runner configuration file. --- act/runner/action.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/act/runner/action.go b/act/runner/action.go index fdfc6373..d27f0a84 100644 --- a/act/runner/action.go +++ b/act/runner/action.go @@ -277,7 +277,6 @@ func removeGitIgnore(ctx context.Context, directory string) error { return nil } -// TODO: break out parts of function to reduce complexicity func execAsDocker(ctx context.Context, step actionStep, actionName, basedir string, localAction bool) error { logger := common.Logger(ctx) rc := step.getRunContext() @@ -291,10 +290,11 @@ func execAsDocker(ctx context.Context, step actionStep, actionName, basedir stri // Apply forcePull only for prebuild docker images forcePull = rc.Config.ForcePull } else { - // "-dockeraction" enshures that "./", "./test " won't get converted to "act-:latest", "act-test-:latest" which are invalid docker image names - image = fmt.Sprintf("%s-dockeraction:%s", regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(actionName, "-"), "latest") - image = fmt.Sprintf("act-%s", strings.TrimLeft(image, "-")) - image = strings.ToLower(image) + if localAction { + image = fmt.Sprintf("runner-local-docker-action-%s:latest", common.MustRandName(16)) + } else { + image = fmt.Sprintf("runner-remote-docker-action-%s:latest", common.Sha256(step.getStepModel().Uses)) + } contextDir, fileName := filepath.Split(filepath.Join(basedir, action.Runs.Image)) anyArchExists, err := container.ImageExistsLocally(ctx, image, "any")