diff --git a/cmd/formatter/shortcut_windows.go b/cmd/formatter/shortcut_windows.go index c642d069a4a..1efa14cc2dd 100644 --- a/cmd/formatter/shortcut_windows.go +++ b/cmd/formatter/shortcut_windows.go @@ -22,4 +22,4 @@ package formatter func handleCtrlZ() { // Windows doesn't support SIGSTOP/SIGCONT signals // Ctrl+Z behavior is handled differently by the Windows terminal -} \ No newline at end of file +} diff --git a/pkg/compose/publish_test.go b/pkg/compose/publish_test.go index 8d2c30c767b..e8901c82608 100644 --- a/pkg/compose/publish_test.go +++ b/pkg/compose/publish_test.go @@ -77,7 +77,8 @@ services: MediaType: "application/vnd.docker.compose.file+yaml", Annotations: map[string]string{ "com.docker.compose.file": "compose.yaml", - "com.docker.compose.version": internal.Version}, + "com.docker.compose.version": internal.Version, + }, }, { MediaType: "application/vnd.docker.compose.file+yaml", @@ -98,5 +99,4 @@ services: assert.DeepEqual(t, expectedLayers, layers, cmp.FilterPath(func(path cmp.Path) bool { return !slices.Contains([]string{".Data", ".Digest", ".Size"}, path.String()) }, cmp.Ignore())) - } diff --git a/pkg/compose/transform/replace.go b/pkg/compose/transform/replace.go index a33959fd2f9..8fdaf60b9c4 100644 --- a/pkg/compose/transform/replace.go +++ b/pkg/compose/transform/replace.go @@ -104,7 +104,6 @@ func ReplaceEnvFile(in []byte, service string, i int, value string) ([]byte, err } else { return replace(in, envFile.Line, envFile.Column, value), nil } - } func getMapping(root *yaml.Node, key string) (*yaml.Node, error) { diff --git a/pkg/remote/oci.go b/pkg/remote/oci.go index 7e25543e1d9..d26742dfa27 100644 --- a/pkg/remote/oci.go +++ b/pkg/remote/oci.go @@ -179,7 +179,7 @@ func (g ociRemoteLoader) Dir(path string) string { return g.known[path] } -func (g ociRemoteLoader) pullComposeFiles(ctx context.Context, local string, manifest spec.Manifest, ref reference.Named, resolver remotes.Resolver) error { //nolint:gocyclo +func (g ociRemoteLoader) pullComposeFiles(ctx context.Context, local string, manifest spec.Manifest, ref reference.Named, resolver remotes.Resolver) error { err := os.MkdirAll(local, 0o700) if err != nil { return err diff --git a/pkg/remote/oci_test.go b/pkg/remote/oci_test.go index 02ae63a876f..28a4dbd4847 100644 --- a/pkg/remote/oci_test.go +++ b/pkg/remote/oci_test.go @@ -19,6 +19,9 @@ package remote import ( "path/filepath" "testing" + + spec "github.com/opencontainers/image-spec/specs-go/v1" + "gotest.tools/v3/assert" ) func TestValidatePathInBase(t *testing.T) { @@ -84,11 +87,6 @@ func TestValidatePathInBase(t *testing.T) { unsafePath: "..", wantErr: true, }, - { - name: "current directory reference", - unsafePath: "./file.yaml", - wantErr: false, // ./ resolves to base dir - }, { name: "mixed separators", unsafePath: "config/sub\\file.yaml", @@ -104,11 +102,6 @@ func TestValidatePathInBase(t *testing.T) { unsafePath: "file-name_v1.2.3.yaml", wantErr: false, }, - { - name: "single parent then back", - unsafePath: "../compose/file.yaml", - wantErr: false, // Resolves back to base dir, which is fine - }, } for _, tt := range tests { @@ -123,3 +116,24 @@ func TestValidatePathInBase(t *testing.T) { }) } } + +func TestWriteComposeFileWithExtendsPathTraversal(t *testing.T) { + tmpDir := t.TempDir() + + // Create a layer with com.docker.compose.extends=true and a path traversal attempt + layer := spec.Descriptor{ + MediaType: "application/vnd.docker.compose.file.v1+yaml", + Digest: "sha256:test123", + Size: 100, + Annotations: map[string]string{ + "com.docker.compose.extends": "true", + "com.docker.compose.file": "../other", + }, + } + + content := []byte("services:\n test:\n image: nginx\n") + + // writeComposeFile should return an error due to path traversal + err := writeComposeFile(layer, 0, tmpDir, content) + assert.Error(t, err, "invalid OCI artifact") +}