diff --git a/tool/shelltool/localshell.go b/tool/shelltool/localshell.go index 3fdc9063..95f015ab 100644 --- a/tool/shelltool/localshell.go +++ b/tool/shelltool/localshell.go @@ -594,37 +594,40 @@ func shellKindDescription(shell resolvedShell) string { } func (s resolvedShell) statelessArgvForCommand(command string) []string { - var suffix []string switch s.kind { case shellKindPowerShell: - suffix = []string{"-NoProfile", "-NoLogo", "-NonInteractive", "-Command", command} + return s.argvWithExtra([]string{"-NoProfile", "-NoLogo", "-NonInteractive", "-Command", command}) case shellKindCmd: - suffix = []string{"/d", "/c", command} + return s.argvWithExtra([]string{"/d", "/c", command}) case shellKindBash: - suffix = []string{"--noprofile", "--norc", "-c", command} + return s.argvWithExtra([]string{"--noprofile", "--norc", "-c", command}) default: - suffix = []string{"-c", command} + return s.argvWithExtra([]string{"-c", command}) } - return combineArgv(s.extraArgv, suffix) } func (s resolvedShell) persistentArgv() ([]string, error) { - var suffix []string switch s.kind { case shellKindPowerShell: - suffix = []string{"-NoProfile", "-NoLogo", "-NonInteractive", "-Command", "-"} + return s.launchArgv([]string{"-NoProfile", "-NoLogo", "-NonInteractive", "-Command", "-"}), nil case shellKindCmd: return nil, fmt.Errorf("persistent mode is not supported for cmd.exe; use pwsh, powershell, or a POSIX shell") case shellKindBash: - suffix = []string{"--noprofile", "--norc"} + return s.launchArgv([]string{"--noprofile", "--norc"}), nil default: - suffix = nil + return s.launchArgv(nil), nil } - return append([]string{s.binary}, combineArgv(s.extraArgv, suffix)...), nil } -func combineArgv(extra, suffix []string) []string { - return slices.Concat(extra, suffix) +func (s resolvedShell) launchArgv(suffix []string) []string { + return append([]string{s.binary}, s.argvWithExtra(suffix)...) +} + +func (s resolvedShell) argvWithExtra(suffix []string) []string { + if len(s.extraArgv) == 0 { + return suffix + } + return append(slices.Clone(s.extraArgv), suffix...) } func classifyShellKind(shell string) shellKind { diff --git a/tool/shelltool/localshell_internal_test.go b/tool/shelltool/localshell_internal_test.go index 9f8d6c12..fcc8a3a3 100644 --- a/tool/shelltool/localshell_internal_test.go +++ b/tool/shelltool/localshell_internal_test.go @@ -2,7 +2,10 @@ package shelltool -import "testing" +import ( + "slices" + "testing" +) func TestPreserveEnvironmentValuesKeepsOnlyAllowlist(t *testing.T) { source := []string{ @@ -11,6 +14,7 @@ func TestPreserveEnvironmentValuesKeepsOnlyAllowlist(t *testing.T) { "AF_SHELL_PARENT_VAR=should-not-leak", "tmp=/tmp/test", } + env := make(map[string]string) preserveEnvironmentValues(env, source) @@ -28,3 +32,23 @@ func TestPreserveEnvironmentValuesKeepsOnlyAllowlist(t *testing.T) { t.Fatal("unexpected non-preserved variable copied") } } + +func TestResolvedShellArgvIncludesExtraArgv(t *testing.T) { + shell := resolvedShell{ + binary: "/custom/bash", + kind: shellKindBash, + extraArgv: []string{"--login"}, + } + + if got, want := shell.statelessArgvForCommand("echo hi"), []string{"--login", "--noprofile", "--norc", "-c", "echo hi"}; !slices.Equal(got, want) { + t.Fatalf("statelessArgvForCommand = %v, want %v", got, want) + } + + got, err := shell.persistentArgv() + if err != nil { + t.Fatalf("persistentArgv: %v", err) + } + if want := []string{"/custom/bash", "--login", "--noprofile", "--norc"}; !slices.Equal(got, want) { + t.Fatalf("persistentArgv = %v, want %v", got, want) + } +}