@@ -5,7 +5,6 @@ package permissions
55import (
66 "fmt"
77 "path/filepath"
8- "strconv"
98 "strings"
109
1110 "github.com/docker/docker-agent/pkg/config/latest"
@@ -85,30 +84,34 @@ func (c *Checker) Check(toolName string) Decision {
8584// (e.g. read-only tools). Note that --yolo mode takes precedence over ForceAsk.
8685func (c * Checker ) CheckWithArgs (toolName string , args map [string ]any ) Decision {
8786 // Deny patterns are checked first - they take priority
88- for _ , pattern := range c .denyPatterns {
89- if matchToolPattern (pattern , toolName , args ) {
90- return Deny
91- }
87+ if matchAny (c .denyPatterns , toolName , args ) {
88+ return Deny
9289 }
9390
9491 // Allow patterns are checked second
95- for _ , pattern := range c .allowPatterns {
96- if matchToolPattern (pattern , toolName , args ) {
97- return Allow
98- }
92+ if matchAny (c .allowPatterns , toolName , args ) {
93+ return Allow
9994 }
10095
10196 // Explicit ask patterns override auto-approval (e.g. read-only hints)
102- for _ , pattern := range c .askPatterns {
103- if matchToolPattern (pattern , toolName , args ) {
104- return ForceAsk
105- }
97+ if matchAny (c .askPatterns , toolName , args ) {
98+ return ForceAsk
10699 }
107100
108101 // Default is Ask
109102 return Ask
110103}
111104
105+ // matchAny reports whether any pattern in the list matches the tool name and args.
106+ func matchAny (patterns []string , toolName string , args map [string ]any ) bool {
107+ for _ , pattern := range patterns {
108+ if matchToolPattern (pattern , toolName , args ) {
109+ return true
110+ }
111+ }
112+ return false
113+ }
114+
112115// IsEmpty returns true if no permissions are configured
113116func (c * Checker ) IsEmpty () bool {
114117 return len (c .allowPatterns ) == 0 && len (c .askPatterns ) == 0 && len (c .denyPatterns ) == 0
@@ -176,12 +179,7 @@ func matchToolPattern(pattern, toolName string, args map[string]any) bool {
176179 return true
177180 }
178181
179- // If pattern has argument conditions but no args provided, no match
180- if args == nil {
181- return false
182- }
183-
184- // All argument patterns must match
182+ // All argument patterns must match (indexing a nil args map is safe in Go)
185183 for argName , argPattern := range argPatterns {
186184 argValue , exists := args [argName ]
187185 if ! exists {
@@ -203,16 +201,9 @@ func argToString(v any) string {
203201 switch val := v .(type ) {
204202 case string :
205203 return val
206- case bool :
207- return strconv .FormatBool (val )
208204 case float64 :
209- // JSON numbers are float64 - format without trailing zeros
210- if val == float64 (int64 (val )) {
211- return strconv .FormatInt (int64 (val ), 10 )
212- }
205+ // JSON numbers are float64 - use %g for shortest representation
213206 return fmt .Sprintf ("%g" , val )
214- case int , int64 :
215- return fmt .Sprintf ("%d" , val )
216207 default :
217208 return fmt .Sprintf ("%v" , v )
218209 }
@@ -237,10 +228,11 @@ func matchGlob(pattern, value string) bool {
237228
238229 // Handle trailing wildcard for prefix matching
239230 // This allows "sudo*" to match "sudo rm -rf /"
240- if strings .HasSuffix (pattern , "*" ) && ! strings . HasSuffix ( pattern , " \\ *" ) {
231+ if strings .HasSuffix (pattern , "*" ) {
241232 prefix := pattern [:len (pattern )- 1 ]
242- // If prefix contains no other glob characters, do simple prefix match
243- if ! strings .ContainsAny (prefix , "*?[" ) {
233+ // If prefix contains no other glob characters, do simple prefix match.
234+ // Including \ catches escaped asterisks (e.g. "foo\*").
235+ if ! strings .ContainsAny (prefix , `*?[\` ) {
244236 return strings .HasPrefix (value , prefix )
245237 }
246238 }
0 commit comments