From 9c10fb3842ee08d5d419c97175bd1a8f3411b389 Mon Sep 17 00:00:00 2001 From: Marko Youngson Date: Thu, 26 Mar 2026 00:49:07 -0600 Subject: [PATCH] fix: restrict cmd template function for remote dependency configs Prevent arbitrary command execution from untrusted remote configuration dependencies by disabling the cmd template function when processing remote configs. --- pkg/skaffold/parser/config.go | 6 ++++++ pkg/skaffold/util/env_template.go | 7 +++++++ pkg/skaffold/util/env_template_test.go | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/pkg/skaffold/parser/config.go b/pkg/skaffold/parser/config.go index 4e87d5c859b..8383486697c 100644 --- a/pkg/skaffold/parser/config.go +++ b/pkg/skaffold/parser/config.go @@ -347,6 +347,12 @@ func processEachDependency(ctx context.Context, d latest.ConfigDependency, cfgOp cfgOpts.file = path cfgOpts.selection = d.Names cfgOpts.isRemote = isRemoteCfg + // Disable the `cmd` template function for remote/untrusted config dependencies + // to prevent arbitrary command execution via malicious skaffold.yaml files. + if isRemoteCfg { + util.CmdAllowed = false + defer func() { util.CmdAllowed = true }() + } depConfigs, _, err := getConfigs(ctx, cfgOpts, opts, r) if err != nil { return nil, err diff --git a/pkg/skaffold/util/env_template.go b/pkg/skaffold/util/env_template.go index c22df79105a..0ebc9668dad 100644 --- a/pkg/skaffold/util/env_template.go +++ b/pkg/skaffold/util/env_template.go @@ -37,6 +37,10 @@ var ( funcsMap = template.FuncMap{ "cmd": runCmdFunc, } + // CmdAllowed controls whether the `cmd` template function is permitted. + // Set to false when processing remote/untrusted config dependencies to + // prevent arbitrary command execution. + CmdAllowed = true ) // ExpandEnvTemplate parses and executes template s with an optional environment map @@ -140,6 +144,9 @@ func MapToFlag(m map[string]*string, flag string) ([]string, error) { } func runCmdFunc(name string, args ...string) (string, error) { + if !CmdAllowed { + return "", fmt.Errorf("the 'cmd' template function is not allowed in remote dependency configs for security reasons") + } cmd := exec.Command(name, args...) out, err := RunCmdOut(context.TODO(), cmd) return strings.TrimSpace(string(out)), err diff --git a/pkg/skaffold/util/env_template_test.go b/pkg/skaffold/util/env_template_test.go index 7582ba4eeb4..2b1b509ed85 100644 --- a/pkg/skaffold/util/env_template_test.go +++ b/pkg/skaffold/util/env_template_test.go @@ -18,6 +18,7 @@ package util import ( "fmt" + "strings" "testing" "github.com/GoogleContainerTools/skaffold/v2/testutil" @@ -180,6 +181,25 @@ func TestMapToFlag(t *testing.T) { } } +func TestCmdBlockedWhenNotAllowed(t *testing.T) { + testutil.Run(t, "cmd blocked in remote config context", func(t *testutil.T) { + t.Override(&CmdAllowed, false) + _, err := ExpandEnvTemplate(`{{cmd "echo" "hello"}}`, nil) + t.CheckError(true, err) + if err != nil && !strings.Contains(err.Error(), "not allowed in remote dependency") { + t.Errorf("expected 'not allowed in remote dependency' error, got: %v", err) + } + }) + + testutil.Run(t, "cmd allowed in local config context", func(t *testutil.T) { + t.Override(&CmdAllowed, true) + t.Override(&DefaultExecCommand, testutil.CmdRunOut("echo hello", "hello")) + out, err := ExpandEnvTemplate(`{{cmd "echo" "hello"}}`, nil) + t.CheckNoError(err) + t.CheckDeepEqual("hello", out) + }) +} + func TestRunCmdFunc(t *testing.T) { tests := []struct { description string