From 1f89349a3a1a36e3d616c89be82197eac79dcc4f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Nov 2026 05:49:05 +0000 Subject: [PATCH 1/4] Initial plan From f40038a1f1254e4a392eeea38781805a9be02888 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Nov 2026 06:09:48 +0000 Subject: [PATCH 2/4] Validate UV executable path before lookup Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/workflow/argument_injection_test.go | 21 +++++++++++++++++++++ pkg/workflow/pip_validation.go | 17 ++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/pkg/workflow/argument_injection_test.go b/pkg/workflow/argument_injection_test.go index 7576a702db4..907b6372ba1 100644 --- a/pkg/workflow/argument_injection_test.go +++ b/pkg/workflow/argument_injection_test.go @@ -588,6 +588,27 @@ printf "%s\n" "$@" > "$GH_AW_TEST_UV_ARGS_FILE" } } +func TestValidateUvPackagesWithExecutable_RejectsUnvalidatedExecutablePath(t *testing.T) { + tempDir := t.TempDir() + argsFile := filepath.Join(tempDir, "uv-args.txt") + uvPath := filepath.Join(tempDir, "uv") + script := `#!/bin/sh +printf "%s\n" "$@" > "$GH_AW_TEST_UV_ARGS_FILE" +` + if err := os.WriteFile(uvPath, []byte(script), 0o755); err != nil { + t.Fatalf("failed to write fake uv executable: %v", err) + } + t.Setenv("GH_AW_TEST_UV_ARGS_FILE", argsFile) + + err := NewCompiler().validateUvPackagesWithExecutable([]string{"requests"}, "uv") + if err == nil { + t.Fatal("expected relative executable path to be rejected") + } + if _, err := os.Stat(argsFile); !os.IsNotExist(err) { + t.Fatalf("expected invalid executable path to be rejected before uv execution; stat error = %v", err) + } +} + // TestValidateUvPackages_RejectsInvalidPackageName verifies that uv package names // which do not conform to the PyPI naming rules are rejected before being passed // as arguments to the uv CLI. This validation happens before uv/pip is resolved diff --git a/pkg/workflow/pip_validation.go b/pkg/workflow/pip_validation.go index f81dcaab05c..ef1a6fee2bc 100644 --- a/pkg/workflow/pip_validation.go +++ b/pkg/workflow/pip_validation.go @@ -231,6 +231,17 @@ func (c *Compiler) validateUvPackages(workflowData *WorkflowData) error { } func (c *Compiler) validateUvPackagesWithExecutable(packages []string, uvPath string) error { + validatedUvPath, err := fileutil.ValidateExecutablePath(uvPath) + if err != nil { + return NewOperationError( + "validate", + "uv packages", + "", + err, + "Install uv to enable package validation:\n\n$ curl -LsSf https://astral.sh/uv/install.sh | sh", + ) + } + var errors []string for _, pkg := range packages { pkgName := stripUvPackageVersion(pkg) @@ -244,10 +255,10 @@ func (c *Compiler) validateUvPackagesWithExecutable(packages []string, uvPath st } // Use uv pip show to check if package exists on PyPI - // #nosec G204 -- uvPath is resolved from the hardcoded executable name "uv" via - // fileutil.ResolveExecutablePath and validated before calling this method; + // #nosec G204 -- uvPath is validated above and resolved from the hardcoded + // executable name "uv" via fileutil.ResolveExecutablePath by the caller; // pkgName is validated above by validatePipPackageName against the strict PEP 508 allowlist. - cmd := exec.Command(uvPath, "pip", "show", "--no-cache", "--", pkgName) + cmd := exec.Command(validatedUvPath, "pip", "show", "--no-cache", "--", pkgName) _, err := cmd.CombinedOutput() if err != nil { From daa618be4d3aa7a2581f74e224e06ede59ec82c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Nov 2026 07:11:06 +0000 Subject: [PATCH 3/4] Make UV path regression test meaningful Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/argument_injection_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/workflow/argument_injection_test.go b/pkg/workflow/argument_injection_test.go index 907b6372ba1..fc69d570309 100644 --- a/pkg/workflow/argument_injection_test.go +++ b/pkg/workflow/argument_injection_test.go @@ -599,8 +599,9 @@ printf "%s\n" "$@" > "$GH_AW_TEST_UV_ARGS_FILE" t.Fatalf("failed to write fake uv executable: %v", err) } t.Setenv("GH_AW_TEST_UV_ARGS_FILE", argsFile) + t.Chdir(tempDir) - err := NewCompiler().validateUvPackagesWithExecutable([]string{"requests"}, "uv") + err := NewCompiler().validateUvPackagesWithExecutable([]string{"requests"}, "./uv") if err == nil { t.Fatal("expected relative executable path to be rejected") } From fcedaeb4adbff9a7ac7ec97e61b40e3f44ea7b73 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Nov 2026 08:39:19 +0000 Subject: [PATCH 4/4] test: match semantic refactor step name Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/workflow/semantic_function_refactor_workflow_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/workflow/semantic_function_refactor_workflow_test.go b/pkg/workflow/semantic_function_refactor_workflow_test.go index 540631e91ef..120d849b3ef 100644 --- a/pkg/workflow/semantic_function_refactor_workflow_test.go +++ b/pkg/workflow/semantic_function_refactor_workflow_test.go @@ -72,7 +72,7 @@ func TestSemanticFunctionRefactorWorkflowCostGuardrails(t *testing.T) { `GH_AW_MAX_AI_CREDITS: "300"`, `\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS}`, `claude-sonnet-5`, - `name: Precompute semantic refactor slice`, + `name: Pre-compute semantic refactor slice`, `/tmp/gh-aw/agent/semantic-function-refactor/analysis.json`, `go run ./scripts/semantic-refactor-slice`, `--max-turns 50`,