From a068005e0786b425ba0505486cfeefd4d02b9583 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 15 May 2026 13:23:33 +0000
Subject: [PATCH 1/2] refactor(config): add envutil.HasEnvVar and
 hasMapKeyVariants helpers

- Add HasEnvVar(key string) bool to internal/envutil to wrap os.LookupEnv
  for presence-only checks, consistent with the envutil abstraction layer
- Replace 4x os.LookupEnv presence checks in guard_policy_parse.go with
  envutil.HasEnvVar calls, removing the direct os import from that file
- Extract hasMapKeyVariants helper to collapse two identical 5-line
  dual-form key check blocks (allow-only/allowonly, write-sink/writesink)
  into single 1-line calls

Closes #5733

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
 internal/config/guard_policy_parse.go | 34 +++++++++++++--------------
 internal/envutil/envutil.go           |  7 ++++++
 2 files changed, 23 insertions(+), 18 deletions(-)

diff --git a/internal/config/guard_policy_parse.go b/internal/config/guard_policy_parse.go
index 9ba663698..30d3a4fc0 100644
--- a/internal/config/guard_policy_parse.go
+++ b/internal/config/guard_policy_parse.go
@@ -3,7 +3,6 @@ package config
 import (
 	"encoding/json"
 	"fmt"
-	"os"
 	"strings"
 
 	"github.com/github/gh-aw-mcpg/internal/envutil"
@@ -70,19 +69,8 @@ func ParsePolicyMap(raw map[string]interface{}) (*GuardPolicy, error) {
 		return nil, nil
 	}
 
-	hasAllowOnly := false
-	if _, ok := raw["allow-only"]; ok {
-		hasAllowOnly = true
-	} else if _, ok := raw["allowonly"]; ok { // Accept legacy "allowonly" form for backward compatibility
-		hasAllowOnly = true
-	}
-
-	hasWriteSink := false
-	if _, ok := raw["write-sink"]; ok {
-		hasWriteSink = true
-	} else if _, ok := raw["writesink"]; ok {
-		hasWriteSink = true
-	}
+	hasAllowOnly := hasMapKeyVariants(raw, "allow-only", "allowonly") // Accept legacy "allowonly" form for backward compatibility
+	hasWriteSink := hasMapKeyVariants(raw, "write-sink", "writesink")
 
 	logGuardPolicy.Printf("ParsePolicyMap: hasAllowOnly=%v, hasWriteSink=%v, keyCount=%d", hasAllowOnly, hasWriteSink, len(raw))
 
@@ -249,10 +237,10 @@ func ResolveGuardPolicyOverride(
 		return policy, "env", nil
 	}
 
-	_, hasScopePublic := os.LookupEnv(EnvAllowOnlyScopePublic)
-	_, hasScopeOwner := os.LookupEnv(EnvAllowOnlyScopeOwner)
-	_, hasScopeRepo := os.LookupEnv(EnvAllowOnlyScopeRepo)
-	_, hasMinIntegrity := os.LookupEnv(EnvAllowOnlyMinIntegrity)
+	hasScopePublic := envutil.HasEnvVar(EnvAllowOnlyScopePublic)
+	hasScopeOwner := envutil.HasEnvVar(EnvAllowOnlyScopeOwner)
+	hasScopeRepo := envutil.HasEnvVar(EnvAllowOnlyScopeRepo)
+	hasMinIntegrity := envutil.HasEnvVar(EnvAllowOnlyMinIntegrity)
 
 	if hasScopePublic || hasScopeOwner || hasScopeRepo || hasMinIntegrity {
 		logGuardPolicy.Printf("ResolveGuardPolicyOverride: using env vars for AllowOnly scope: hasScopePublic=%v, hasScopeOwner=%v, hasScopeRepo=%v, hasMinIntegrity=%v",
@@ -291,3 +279,13 @@ func NormalizeScopeKind(policy map[string]interface{}) map[string]interface{} {
 
 	return normalized
 }
+
+// hasMapKeyVariants reports whether any of the given key variants is present in m.
+func hasMapKeyVariants(m map[string]interface{}, keys ...string) bool {
+	for _, k := range keys {
+		if _, ok := m[k]; ok {
+			return true
+		}
+	}
+	return false
+}
diff --git a/internal/envutil/envutil.go b/internal/envutil/envutil.go
index 609ba67a7..9a85dedd1 100644
--- a/internal/envutil/envutil.go
+++ b/internal/envutil/envutil.go
@@ -12,6 +12,13 @@ import (
 
 var logEnvUtil = logger.New("envutil:envutil")
 
+// HasEnvVar reports whether the named environment variable is present in the
+// process environment, regardless of whether its value is empty.
+func HasEnvVar(key string) bool {
+	_, ok := os.LookupEnv(key)
+	return ok
+}
+
 // GetEnvString returns the value of the environment variable specified by envKey.
 // If the environment variable is not set or is empty, it returns the defaultValue.
 func GetEnvString(envKey, defaultValue string) string {

From 126dfd252a4773b6fa1ade50f2626d453fa3f076 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 15 May 2026 13:33:58 +0000
Subject: [PATCH 2/2] test(envutil): cover HasEnvVar unset and empty cases

---
 internal/envutil/envutil_test.go | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/internal/envutil/envutil_test.go b/internal/envutil/envutil_test.go
index 9caac5039..4131eb529 100644
--- a/internal/envutil/envutil_test.go
+++ b/internal/envutil/envutil_test.go
@@ -8,6 +8,18 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+func TestHasEnvVar(t *testing.T) {
+	t.Run("unset returns false", func(t *testing.T) {
+		os.Unsetenv("TEST_HAS_ENV_VAR")
+		assert.False(t, HasEnvVar("TEST_HAS_ENV_VAR"))
+	})
+
+	t.Run("set to empty string returns true", func(t *testing.T) {
+		t.Setenv("TEST_HAS_ENV_VAR", "")
+		assert.True(t, HasEnvVar("TEST_HAS_ENV_VAR"))
+	})
+}
+
 func TestGetEnvDuration(t *testing.T) {
 	tests := []struct {
 		name         string
