From 2a1b1ede0df35d3eaff8c82684ff737cdb7ae5d0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:57:50 +0000 Subject: [PATCH 1/2] Initial plan From ecdd2fa6010eabd0d47329ad9e3139e655bc5baf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:06:28 +0000 Subject: [PATCH 2/2] fix: URL-path-escape endpoint segments in ghAPIGet and ghAPIGetArray Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/outcome_eval.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pkg/cli/outcome_eval.go b/pkg/cli/outcome_eval.go index 51f75f60e82..47c3487039c 100644 --- a/pkg/cli/outcome_eval.go +++ b/pkg/cli/outcome_eval.go @@ -248,6 +248,16 @@ func validateAPIEndpoint(endpoint string) error { return nil } +// escapeEndpoint URL-path-encodes each segment of an endpoint string to +// prevent path injection when the value is interpolated into an API URL. +func escapeEndpoint(endpoint string) string { + parts := strings.Split(endpoint, "/") + for i, p := range parts { + parts[i] = url.PathEscape(p) + } + return strings.Join(parts, "/") +} + // ghAPIGet calls the GitHub REST API via gh cli and returns the parsed JSON. func ghAPIGet(ctx context.Context, endpoint string, repo string) (map[string]any, error) { if err := validateAPIEndpoint(endpoint); err != nil { @@ -255,7 +265,7 @@ func ghAPIGet(ctx context.Context, endpoint string, repo string) (map[string]any } ownerRepo, host := repoutil.NormalizeRepoForAPI(repo) outcomeEvalLog.Printf("gh api GET: repo=%s, endpoint=%s, host=%q", ownerRepo, endpoint, host) - args := []string{"api", fmt.Sprintf("repos/%s/%s", escapeOwnerRepo(ownerRepo), endpoint)} + args := []string{"api", fmt.Sprintf("repos/%s/%s", escapeOwnerRepo(ownerRepo), escapeEndpoint(endpoint))} var output []byte var err error if host != "" { @@ -280,7 +290,7 @@ func ghAPIGetArray(ctx context.Context, endpoint string, repo string) ([]map[str return nil, fmt.Errorf("invalid endpoint %q: %w", endpoint, err) } ownerRepo, host := repoutil.NormalizeRepoForAPI(repo) - args := []string{"api", fmt.Sprintf("repos/%s/%s", escapeOwnerRepo(ownerRepo), endpoint)} + args := []string{"api", fmt.Sprintf("repos/%s/%s", escapeOwnerRepo(ownerRepo), escapeEndpoint(endpoint))} var output []byte var err error if host != "" {