From 18d2b9ef62e40463332156d5e85f6c5b9a0f3418 Mon Sep 17 00:00:00 2001 From: CodeLoopdroid <214800619+CodeLoopdroid@users.noreply.github.com> Date: Thu, 26 Mar 2026 21:36:38 +0530 Subject: [PATCH 1/4] Fix publish short-form port mappings Signed-off-by: CodeLoopdroid <214800619+CodeLoopdroid@users.noreply.github.com> --- pkg/compose/publish.go | 5 +++-- pkg/compose/publish_test.go | 30 ++++++++++++++++++++++++++++++ pkg/e2e/publish_test.go | 19 +++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/pkg/compose/publish.go b/pkg/compose/publish.go index 06d6eb4d136..232350b9086 100644 --- a/pkg/compose/publish.go +++ b/pkg/compose/publish.go @@ -36,6 +36,7 @@ import ( "github.com/opencontainers/image-spec/specs-go" v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" + "go.yaml.in/yaml/v4" "github.com/docker/compose/v5/internal/oci" "github.com/docker/compose/v5/pkg/api" @@ -476,7 +477,7 @@ func composeFileAsByteReader(ctx context.Context, filePath string, project *type if err != nil { return nil, fmt.Errorf("failed to open compose file %s: %w", filePath, err) } - base, err := loader.LoadWithContext(ctx, types.ConfigDetails{ + model, err := loader.LoadModelWithContext(ctx, types.ConfigDetails{ WorkingDir: project.WorkingDir, Environment: project.Environment, ConfigFiles: []types.ConfigFile{ @@ -497,7 +498,7 @@ func composeFileAsByteReader(ctx context.Context, filePath string, project *type return nil, err } - in, err := base.MarshalYAML() + in, err := yaml.Marshal(model) if err != nil { return nil, err } diff --git a/pkg/compose/publish_test.go b/pkg/compose/publish_test.go index 8f91f663e69..940c27fa0a1 100644 --- a/pkg/compose/publish_test.go +++ b/pkg/compose/publish_test.go @@ -17,6 +17,8 @@ package compose import ( + "os" + "path/filepath" "slices" "testing" @@ -100,3 +102,31 @@ services: return !slices.Contains([]string{".Data", ".Digest", ".Size"}, path.String()) }, cmp.Ignore())) } + +func TestComposeFileAsByteReaderWithShortFormPortsAndInterpolation(t *testing.T) { + dir := t.TempDir() + composePath := filepath.Join(dir, "compose.yaml") + err := os.WriteFile(composePath, []byte(`name: publish-test +services: + whoami: + image: docker.io/traefik/whoami:v1.11 + ports: + - ${DASHBOARD_PORT:-3000}:3000 +`), 0o600) + assert.NilError(t, err) + + project, err := loader.LoadWithContext(t.Context(), types.ConfigDetails{ + WorkingDir: dir, + Environment: types.Mapping{ + "DASHBOARD_PORT": "", + }, + ConfigFiles: []types.ConfigFile{ + {Filename: composePath}, + }, + }) + assert.NilError(t, err) + + reader, err := composeFileAsByteReader(t.Context(), composePath, project) + assert.NilError(t, err) + assert.Assert(t, reader != nil) +} diff --git a/pkg/e2e/publish_test.go b/pkg/e2e/publish_test.go index b5488df601c..1d668c46a1f 100644 --- a/pkg/e2e/publish_test.go +++ b/pkg/e2e/publish_test.go @@ -18,6 +18,8 @@ package e2e import ( "fmt" + "os" + "path/filepath" "strings" "testing" @@ -60,6 +62,23 @@ or remove sensitive data from your Compose configuration assert.Assert(t, strings.Contains(res.Combined(), "test/test published"), res.Combined()) }) + t.Run("publish success short-form port mapping", func(t *testing.T) { + dir := t.TempDir() + composePath := filepath.Join(dir, "compose-short-port.yml") + err := os.WriteFile(composePath, []byte(`services: + whoami: + image: docker.io/traefik/whoami:v1.11 + ports: + - ${DASHBOARD_PORT:-3000}:3000 +`), 0o600) + assert.NilError(t, err) + + res := c.RunDockerComposeCmd(t, "-f", composePath, + "-p", projectName, "publish", "test/test", "--with-env", "-y", "--dry-run") + assert.Assert(t, strings.Contains(res.Combined(), "test/test publishing"), res.Combined()) + assert.Assert(t, strings.Contains(res.Combined(), "test/test published"), res.Combined()) + }) + t.Run("publish with extends", func(t *testing.T) { res := c.RunDockerComposeCmd(t, "-f", "./fixtures/publish/compose-with-extends.yml", "-p", projectName, "publish", "test/test", "--dry-run") From 7df4329d778add315728e4b3c81b26d61d565cd9 Mon Sep 17 00:00:00 2001 From: CodeLoopdroid <214800619+CodeLoopdroid@users.noreply.github.com> Date: Fri, 27 Mar 2026 15:15:24 +0530 Subject: [PATCH 2/4] Fix yaml import shadowing Signed-off-by: CodeLoopdroid <214800619+CodeLoopdroid@users.noreply.github.com> --- pkg/compose/publish.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/compose/publish.go b/pkg/compose/publish.go index 232350b9086..80708b9b9b0 100644 --- a/pkg/compose/publish.go +++ b/pkg/compose/publish.go @@ -184,12 +184,12 @@ func (s *composeService) createLayers(ctx context.Context, project *types.Projec } if options.ResolveImageDigests { - yaml, err := s.generateImageDigestsOverride(ctx, project) + overrideYAML, err := s.generateImageDigestsOverride(ctx, project) if err != nil { return nil, err } - layerDescriptor := oci.DescriptorForComposeFile("image-digests.yaml", yaml) + layerDescriptor := oci.DescriptorForComposeFile("image-digests.yaml", overrideYAML) layers = append(layers, layerDescriptor) } return layers, nil From 1b4554ff3b85587613c62fdc46ed9b4266668cfc Mon Sep 17 00:00:00 2001 From: CodeLoopdroid <214800619+CodeLoopdroid@users.noreply.github.com> Date: Fri, 27 Mar 2026 15:34:09 +0530 Subject: [PATCH 3/4] Fix publish sensitive data detection Signed-off-by: CodeLoopdroid <214800619+CodeLoopdroid@users.noreply.github.com> --- pkg/compose/publish.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/pkg/compose/publish.go b/pkg/compose/publish.go index 80708b9b9b0..8c7928108bc 100644 --- a/pkg/compose/publish.go +++ b/pkg/compose/publish.go @@ -477,7 +477,7 @@ func composeFileAsByteReader(ctx context.Context, filePath string, project *type if err != nil { return nil, fmt.Errorf("failed to open compose file %s: %w", filePath, err) } - model, err := loader.LoadModelWithContext(ctx, types.ConfigDetails{ + configDetails := types.ConfigDetails{ WorkingDir: project.WorkingDir, Environment: project.Environment, ConfigFiles: []types.ConfigFile{ @@ -486,18 +486,29 @@ func composeFileAsByteReader(ctx context.Context, filePath string, project *type Content: composeFile, }, }, - }, func(options *loader.Options) { + } + loadOptions := func(options *loader.Options) { options.SkipValidation = true options.SkipExtends = true options.SkipConsistencyCheck = true options.ResolvePaths = true options.SkipInterpolation = true options.SkipResolveEnvironment = true - }) + } + + base, err := loader.LoadWithContext(ctx, configDetails, loadOptions) + if err == nil { + in, err := base.MarshalYAML() + if err != nil { + return nil, err + } + return bytes.NewBuffer(in), nil + } + + model, err := loader.LoadModelWithContext(ctx, configDetails, loadOptions) if err != nil { return nil, err } - in, err := yaml.Marshal(model) if err != nil { return nil, err From fda315a7af58aa0c55ec6130b7ab060c81fb9955 Mon Sep 17 00:00:00 2001 From: CodeLoopdroid <214800619+CodeLoopdroid@users.noreply.github.com> Date: Fri, 27 Mar 2026 21:49:26 +0530 Subject: [PATCH 4/4] Fix publish environment secret scan Signed-off-by: CodeLoopdroid <214800619+CodeLoopdroid@users.noreply.github.com> --- pkg/compose/publish.go | 51 +++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/pkg/compose/publish.go b/pkg/compose/publish.go index 8f86d37acdb..7c8499edb33 100644 --- a/pkg/compose/publish.go +++ b/pkg/compose/publish.go @@ -437,6 +437,39 @@ func (s *composeService) checkForSensitiveData(ctx context.Context, project *typ } allFindings = append(allFindings, findings...) } + for _, service := range project.Services { + if len(service.Environment) == 0 { + continue + } + + environment := map[string]string{} + for key, value := range service.Environment { + if value == nil { + continue + } + environment[key] = *value + } + if len(environment) == 0 { + continue + } + + in, err := yaml.Marshal(map[string]any{ + "services": map[string]any{ + service.Name: map[string]any{ + "environment": environment, + }, + }, + }) + if err != nil { + return nil, err + } + + findings, err := scan.ScanReader(bytes.NewReader(in)) + if err != nil { + return nil, fmt.Errorf("failed to scan environment for service %s: %w", service.Name, err) + } + allFindings = append(allFindings, findings...) + } for _, service := range project.Services { // Check env files for _, envFile := range service.EnvFiles { @@ -478,7 +511,7 @@ func composeFileAsByteReader(ctx context.Context, filePath string, project *type if err != nil { return nil, fmt.Errorf("failed to open compose file %s: %w", filePath, err) } - configDetails := types.ConfigDetails{ + model, err := loader.LoadModelWithContext(ctx, types.ConfigDetails{ WorkingDir: project.WorkingDir, Environment: project.Environment, ConfigFiles: []types.ConfigFile{ @@ -487,26 +520,14 @@ func composeFileAsByteReader(ctx context.Context, filePath string, project *type Content: composeFile, }, }, - } - loadOptions := func(options *loader.Options) { + }, func(options *loader.Options) { options.SkipValidation = true options.SkipExtends = true options.SkipConsistencyCheck = true options.ResolvePaths = true options.SkipInterpolation = true options.SkipResolveEnvironment = true - } - - base, err := loader.LoadWithContext(ctx, configDetails, loadOptions) - if err == nil { - in, err := base.MarshalYAML() - if err != nil { - return nil, err - } - return bytes.NewBuffer(in), nil - } - - model, err := loader.LoadModelWithContext(ctx, configDetails, loadOptions) + }) if err != nil { return nil, err }