Skip to content

Commit c36892a

Browse files
authored
feat(use_aws): add configurable autoAllowReadonly setting (#2828)
- Add auto_allow_readonly field to use_aws Settings struct (defaults to false) - Update eval_perm method to use auto_allow_readonly setting instead of hardcoded behavior - Default behavior: all AWS operations require user confirmation (secure by default) - Opt-in behavior: when autoAllowReadonly=true, read-only operations are auto-approved - Add comprehensive tests covering all scenarios - Maintains backward compatibility through configuration 🤖 Assisted by Amazon Q Developer Co-authored-by: Matt Lee <[email protected]>
1 parent 5ea7045 commit c36892a

File tree

2 files changed

+123
-6
lines changed

2 files changed

+123
-6
lines changed

crates/chat-cli/src/cli/chat/tools/use_aws.rs

Lines changed: 120 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

docs/built-in-tools.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ Make AWS CLI API calls with the specified service, operation, and parameters.
139139
"toolsSettings": {
140140
"use_aws": {
141141
"allowedServices": ["s3", "lambda", "ec2"],
142-
"deniedServices": ["eks", "rds"]
142+
"deniedServices": ["eks", "rds"],
143+
"autoAllowReadonly": true
143144
}
144145
}
145146
}
@@ -151,6 +152,7 @@ Make AWS CLI API calls with the specified service, operation, and parameters.
151152
|--------|------|---------|-------------|
152153
| `allowedServices` | array of strings | `[]` | List of AWS services that can be accessed without prompting |
153154
| `deniedServices` | array of strings | `[]` | List of AWS services to deny. Deny rules are evaluated before allow rules |
155+
| `autoAllowReadonly` | boolean | `false` | Whether to automatically allow read-only operations (get, describe, list, ls, search, batch_get) without prompting |
154156

155157
## Using Tool Settings in Agent Configuration
156158

0 commit comments

Comments
 (0)