From 333d3a746aab6862d0f3c0b56bc5208be4c4198b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:25:31 +0000 Subject: [PATCH 1/3] Initial plan From 68e21d2b26a0de0eaa97ed0d8864cf6310dd8ea0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:41:38 +0000 Subject: [PATCH 2/3] refactor: deduplicate []string to []interface{} conversions --- internal/config/guard_policy_validation.go | 8 +++---- internal/guard/wasm_payload.go | 13 +++-------- internal/strutil/strings_to_any_test.go | 26 ++++++++++++++++++++++ internal/strutil/strutil.go | 9 ++++++++ 4 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 internal/strutil/strings_to_any_test.go diff --git a/internal/config/guard_policy_validation.go b/internal/config/guard_policy_validation.go index 03dca6c91..c3d46fe2e 100644 --- a/internal/config/guard_policy_validation.go +++ b/internal/config/guard_policy_validation.go @@ -5,6 +5,8 @@ import ( "fmt" "sort" "strings" + + "github.com/github/gh-aw-mcpg/internal/strutil" ) const errMsgPolicyMissingKey = "policy must include allow-only or write-sink" @@ -186,11 +188,7 @@ func NormalizeGuardPolicy(policy *GuardPolicy) (*NormalizedGuardPolicy, error) { return normalized, nil case []string: - generic := make([]interface{}, len(scope)) - for i := range scope { - generic[i] = scope[i] - } - scopes, err := normalizeAndValidateScopeArray(generic) + scopes, err := normalizeAndValidateScopeArray(strutil.StringsToAny(scope)) if err != nil { return nil, err } diff --git a/internal/guard/wasm_payload.go b/internal/guard/wasm_payload.go index 960123df0..d34b6fc3a 100644 --- a/internal/guard/wasm_payload.go +++ b/internal/guard/wasm_payload.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/github/gh-aw-mcpg/internal/config" + "github.com/github/gh-aw-mcpg/internal/strutil" ) // normalizePolicyPayload coerces a policy value to a map[string]interface{}. @@ -250,24 +251,16 @@ func BuildLabelAgentPayload(policy interface{}, trustedBots []string, trustedUse if len(trustedBots) > 0 { // trusted-bots is a top-level key in the label_agent payload. - // Convert []string to []interface{} for JSON compatibility. - bots := make([]interface{}, len(trustedBots)) - for i, b := range trustedBots { - bots[i] = b - } + bots := strutil.StringsToAny(trustedBots) payload["trusted-bots"] = bots logWasm.Printf("BuildLabelAgentPayload: injected %d trusted-bots into payload", len(trustedBots)) } if len(trustedUsers) > 0 { // trusted-users is injected inside the allow-only object. - // Convert []string to []interface{} for JSON compatibility. // If allow-only is absent, the injection is skipped and buildStrictLabelAgentPayload // will reject the payload when called with the missing allow-only key. - users := make([]interface{}, len(trustedUsers)) - for i, u := range trustedUsers { - users[i] = u - } + users := strutil.StringsToAny(trustedUsers) // Inject into allow-only object if present if allowOnly, ok := payload["allow-only"].(map[string]interface{}); ok { allowOnly["trusted-users"] = users diff --git a/internal/strutil/strings_to_any_test.go b/internal/strutil/strings_to_any_test.go new file mode 100644 index 000000000..4dc1bc9a6 --- /dev/null +++ b/internal/strutil/strings_to_any_test.go @@ -0,0 +1,26 @@ +package strutil + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStringsToAny(t *testing.T) { + t.Parallel() + + t.Run("nil input returns empty slice", func(t *testing.T) { + t.Parallel() + assert.Empty(t, StringsToAny(nil)) + }) + + t.Run("empty input returns empty slice", func(t *testing.T) { + t.Parallel() + assert.Empty(t, StringsToAny([]string{})) + }) + + t.Run("converts all entries preserving order", func(t *testing.T) { + t.Parallel() + assert.Equal(t, []interface{}{"octo", "hub", "bot"}, StringsToAny([]string{"octo", "hub", "bot"})) + }) +} diff --git a/internal/strutil/strutil.go b/internal/strutil/strutil.go index 1d6429156..9dc59fa42 100644 --- a/internal/strutil/strutil.go +++ b/internal/strutil/strutil.go @@ -28,6 +28,15 @@ func DeduplicateStrings(input []string, sorted bool) []string { return out } +// StringsToAny converts a []string to []interface{}. +func StringsToAny(input []string) []interface{} { + out := make([]interface{}, len(input)) + for i, value := range input { + out[i] = value + } + return out +} + // GetStringFromMap returns the first non-empty string value found for any of // the given keys in m. For each key, the value must be present, typed as // string, and non-empty to be returned. Returns an empty string when no From 0b5a8bbb22c7051d10777829c7eb1a3fc03ab675 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Tue, 23 Jun 2026 15:14:09 -0700 Subject: [PATCH 3/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- internal/strutil/strings_to_any_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/strutil/strings_to_any_test.go b/internal/strutil/strings_to_any_test.go index 4dc1bc9a6..5121e6db5 100644 --- a/internal/strutil/strings_to_any_test.go +++ b/internal/strutil/strings_to_any_test.go @@ -9,9 +9,9 @@ import ( func TestStringsToAny(t *testing.T) { t.Parallel() - t.Run("nil input returns empty slice", func(t *testing.T) { + t.Run("nil input returns empty (non-nil) slice", func(t *testing.T) { t.Parallel() - assert.Empty(t, StringsToAny(nil)) + assert.Equal(t, []interface{}{}, StringsToAny(nil)) }) t.Run("empty input returns empty slice", func(t *testing.T) {