Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions internal/config/guard_policy_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand Down
13 changes: 3 additions & 10 deletions internal/guard/wasm_payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}.
Expand Down Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions internal/strutil/strings_to_any_test.go
Original file line number Diff line number Diff line change
@@ -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 (non-nil) slice", func(t *testing.T) {
t.Parallel()
assert.Equal(t, []interface{}{}, 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"}))
})
}
9 changes: 9 additions & 0 deletions internal/strutil/strutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading