@@ -182,6 +182,8 @@ impl UseAws {
182182 allowed_services : Vec < String > ,
183183 #[ serde( default ) ]
184184 denied_services : Vec < String > ,
185+ #[ serde( default ) ]
186+ auto_allow_readonly : bool ,
185187 }
186188
187189 let Self { service_name, .. } = self ;
@@ -201,15 +203,16 @@ impl UseAws {
201203 if is_in_allowlist || settings. allowed_services . contains ( service_name) {
202204 return PermissionEvalResult :: Allow ;
203205 }
206+ // Check auto_allow_readonly setting for read-only operations
207+ if settings. auto_allow_readonly && !self . requires_acceptance ( ) {
208+ return PermissionEvalResult :: Allow ;
209+ }
204210 PermissionEvalResult :: Ask
205211 } ,
206212 None if is_in_allowlist => PermissionEvalResult :: Allow ,
207213 _ => {
208- if self . requires_acceptance ( ) {
209- PermissionEvalResult :: Ask
210- } else {
211- PermissionEvalResult :: Allow
212- }
214+ // Default behavior: always ask for confirmation (no auto-approval for read-only)
215+ PermissionEvalResult :: Ask
213216 } ,
214217 }
215218 }
@@ -390,4 +393,116 @@ mod tests {
390393 let res = cmd_one. eval_perm ( & os, & agent) ;
391394 assert ! ( matches!( res, PermissionEvalResult :: Deny ( ref services) if services. contains( & "s3" . to_string( ) ) ) ) ;
392395 }
396+
397+ #[ tokio:: test]
398+ async fn test_eval_perm_auto_allow_readonly_default ( ) {
399+ let os = Os :: new ( ) . await . unwrap ( ) ;
400+
401+ // Test read-only operation with default settings (auto_allow_readonly = false)
402+ let readonly_cmd = use_aws ! { {
403+ "service_name" : "s3" ,
404+ "operation_name" : "list-objects" ,
405+ "region" : "us-west-2" ,
406+ "profile_name" : "default" ,
407+ "label" : ""
408+ } } ;
409+
410+ let agent = Agent :: default ( ) ;
411+ let res = readonly_cmd. eval_perm ( & os, & agent) ;
412+ // Should ask for confirmation even for read-only operations by default
413+ assert ! ( matches!( res, PermissionEvalResult :: Ask ) ) ;
414+
415+ // Test write operation with default settings
416+ let write_cmd = use_aws ! { {
417+ "service_name" : "s3" ,
418+ "operation_name" : "put-object" ,
419+ "region" : "us-west-2" ,
420+ "profile_name" : "default" ,
421+ "label" : ""
422+ } } ;
423+
424+ let res = write_cmd. eval_perm ( & os, & agent) ;
425+ // Should ask for confirmation for write operations
426+ assert ! ( matches!( res, PermissionEvalResult :: Ask ) ) ;
427+ }
428+
429+ #[ tokio:: test]
430+ async fn test_eval_perm_auto_allow_readonly_enabled ( ) {
431+ let os = Os :: new ( ) . await . unwrap ( ) ;
432+
433+ let agent = Agent {
434+ name : "test_agent" . to_string ( ) ,
435+ tools_settings : {
436+ let mut map = HashMap :: < ToolSettingTarget , serde_json:: Value > :: new ( ) ;
437+ map. insert (
438+ ToolSettingTarget ( "use_aws" . to_string ( ) ) ,
439+ serde_json:: json!( {
440+ "autoAllowReadonly" : true
441+ } ) ,
442+ ) ;
443+ map
444+ } ,
445+ ..Default :: default ( )
446+ } ;
447+
448+ // Test read-only operation with auto_allow_readonly = true
449+ let readonly_cmd = use_aws ! { {
450+ "service_name" : "s3" ,
451+ "operation_name" : "list-objects" ,
452+ "region" : "us-west-2" ,
453+ "profile_name" : "default" ,
454+ "label" : ""
455+ } } ;
456+
457+ let res = readonly_cmd. eval_perm ( & os, & agent) ;
458+ // Should allow read-only operations without confirmation
459+ assert ! ( matches!( res, PermissionEvalResult :: Allow ) ) ;
460+
461+ // Test write operation with auto_allow_readonly = true
462+ let write_cmd = use_aws ! { {
463+ "service_name" : "s3" ,
464+ "operation_name" : "put-object" ,
465+ "region" : "us-west-2" ,
466+ "profile_name" : "default" ,
467+ "label" : ""
468+ } } ;
469+
470+ let res = write_cmd. eval_perm ( & os, & agent) ;
471+ // Should still ask for confirmation for write operations
472+ assert ! ( matches!( res, PermissionEvalResult :: Ask ) ) ;
473+ }
474+
475+ #[ tokio:: test]
476+ async fn test_eval_perm_auto_allow_readonly_with_denied_services ( ) {
477+ let os = Os :: new ( ) . await . unwrap ( ) ;
478+
479+ let agent = Agent {
480+ name : "test_agent" . to_string ( ) ,
481+ tools_settings : {
482+ let mut map = HashMap :: < ToolSettingTarget , serde_json:: Value > :: new ( ) ;
483+ map. insert (
484+ ToolSettingTarget ( "use_aws" . to_string ( ) ) ,
485+ serde_json:: json!( {
486+ "autoAllowReadonly" : true ,
487+ "deniedServices" : [ "s3" ]
488+ } ) ,
489+ ) ;
490+ map
491+ } ,
492+ ..Default :: default ( )
493+ } ;
494+
495+ // Test read-only operation on denied service
496+ let readonly_cmd = use_aws ! { {
497+ "service_name" : "s3" ,
498+ "operation_name" : "list-objects" ,
499+ "region" : "us-west-2" ,
500+ "profile_name" : "default" ,
501+ "label" : ""
502+ } } ;
503+
504+ let res = readonly_cmd. eval_perm ( & os, & agent) ;
505+ // Should deny even read-only operations on denied services
506+ assert ! ( matches!( res, PermissionEvalResult :: Deny ( ref services) if services. contains( & "s3" . to_string( ) ) ) ) ;
507+ }
393508}
0 commit comments