diff --git a/act/container/docker_run.go b/act/container/docker_run.go index 502d4828..f3a279c8 100644 --- a/act/container/docker_run.go +++ b/act/container/docker_run.go @@ -868,11 +868,12 @@ func (cr *containerReference) CopyTarStream(ctx context.Context, destPath string if cr.id == "" { return cr.missingContainerError("copy to %s", destPath) } - // Mkdir + // Mkdir, with a path relative to the DestinationPath ("/") below. Docker 29.5+ + // rejects absolute tar entry names with "path escapes from parent". buf := &bytes.Buffer{} tw := tar.NewWriter(buf) _ = tw.WriteHeader(&tar.Header{ - Name: destPath, + Name: strings.TrimPrefix(destPath, "/"), Mode: 0o777, Typeflag: tar.TypeDir, }) diff --git a/act/container/docker_run_test.go b/act/container/docker_run_test.go index bc556509..43ea19fb 100644 --- a/act/container/docker_run_test.go +++ b/act/container/docker_run_test.go @@ -5,6 +5,7 @@ package container import ( + "archive/tar" "bufio" "bytes" "context" @@ -358,6 +359,44 @@ func TestDockerCopyTarStream(t *testing.T) { client.AssertExpectations(t) } +// Docker 29.5+ rejects absolute names in the mkdir tarball with +// "path escapes from parent", since it is extracted relative to "/". +func TestDockerCopyTarStreamMkdirEntryIsRelative(t *testing.T) { + ctx := context.Background() + + var mkdirNames []string + client := &mockDockerClient{} + client.On("CopyToContainer", ctx, "123", mock.MatchedBy(func(opts mobyclient.CopyToContainerOptions) bool { + if opts.DestinationPath != "/" || opts.Content == nil { + return false + } + tr := tar.NewReader(opts.Content) + for { + hdr, err := tr.Next() + if err != nil { + break + } + mkdirNames = append(mkdirNames, hdr.Name) + } + return true + })).Return(mobyclient.CopyToContainerResult{}, nil) + client.On("CopyToContainer", ctx, "123", mock.MatchedBy(func(opts mobyclient.CopyToContainerOptions) bool { + return opts.DestinationPath == "/var/run/act" && opts.Content != nil + })).Return(mobyclient.CopyToContainerResult{}, nil) + cr := &containerReference{ + id: "123", + cli: client, + input: &NewContainerInput{ + Image: "image", + }, + } + + require.NoError(t, cr.CopyTarStream(ctx, "/var/run/act", &bytes.Buffer{})) + assert.Equal(t, []string{"var/run/act"}, mkdirNames) + + client.AssertExpectations(t) +} + func TestDockerCopyTarStreamErrorInCopyFiles(t *testing.T) { ctx := context.Background()