From 2fd4ba3d5a10f5c13bdd4249685e51711507580f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:49:09 +0000 Subject: [PATCH 1/2] Initial plan From 86f894e0ae52fc81a35e4d6511cf92ee6306f724 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:03:38 +0000 Subject: [PATCH 2/2] test command argument injection safeguards Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/upgrade_command_test.go | 29 +++++++++++++++++++++++++ pkg/workflow/argument_injection_test.go | 20 +++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/pkg/cli/upgrade_command_test.go b/pkg/cli/upgrade_command_test.go index 6a2aa3c6f2f..84412f8e11b 100644 --- a/pkg/cli/upgrade_command_test.go +++ b/pkg/cli/upgrade_command_test.go @@ -4,6 +4,7 @@ package cli import ( "context" + "io" "os" "testing" @@ -165,6 +166,34 @@ func TestRelaunchWithSameArgsAllowsEmptyForwardedArgument(t *testing.T) { require.NoError(t, err) } +func TestRelaunchWithSameArgsPassesShellMetacharactersLiterally(t *testing.T) { + origArgs := os.Args + origStdout := os.Stdout + t.Cleanup(func() { + os.Args = origArgs + os.Stdout = origStdout + }) + os.Args = []string{"gh-aw", "upgrade", ";", "$(whoami)"} + + readOutput, writeOutput, err := os.Pipe() + require.NoError(t, err) + writeOutputClosed := false + t.Cleanup(func() { + _ = readOutput.Close() + if !writeOutputClosed { + _ = writeOutput.Close() + } + }) + os.Stdout = writeOutput + + require.NoError(t, relaunchWithSameArgs("--skip-extension-upgrade", "/bin/echo")) + require.NoError(t, writeOutput.Close()) + writeOutputClosed = true + output, err := io.ReadAll(readOutput) + require.NoError(t, err) + require.Equal(t, "upgrade ; $(whoami) --skip-extension-upgrade\n", string(output)) +} + func TestRelaunchWithSameArgsRejectsUnknownExtraFlag(t *testing.T) { err := relaunchWithSameArgs("--unknown-flag", "/bin/echo") require.Error(t, err) diff --git a/pkg/workflow/argument_injection_test.go b/pkg/workflow/argument_injection_test.go index 3dfec340e2a..0f79b59e1a1 100644 --- a/pkg/workflow/argument_injection_test.go +++ b/pkg/workflow/argument_injection_test.go @@ -370,6 +370,26 @@ func TestValidatePipPackageName(t *testing.T) { expectError: true, errContains: "PyPI names must start and end with a letter or digit, with hyphens, underscores, or dots allowed inside (e.g. \"requests\" or \"my-package\")", }, + { + name: "shell separator is rejected", + pkg: "pkg;whoami", + expectError: true, + }, + { + name: "command substitution is rejected", + pkg: "pkg$(whoami)", + expectError: true, + }, + { + name: "newline option injection is rejected", + pkg: "pkg\n--index-url", + expectError: true, + }, + { + name: "empty name is rejected", + pkg: "", + expectError: true, + }, } for _, tt := range tests {