mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
fix(subagents) share execpolicy by default (#13702)
## Summary If a subagent requests approval, and the user persists that approval to the execpolicy, it should (by default) propagate. We'll need to rethink this a bit in light of coming Permissions changes, though I think this is closer to the end state that we'd want, which is that execpolicy changes to one permissions profile should be synced across threads. ## Testing - [x] Added integration test --------- Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -1,9 +1,16 @@
|
||||
use super::*;
|
||||
use crate::config::Config;
|
||||
use crate::config::ConfigBuilder;
|
||||
use crate::config_loader::ConfigLayerEntry;
|
||||
use crate::config_loader::ConfigLayerStack;
|
||||
use crate::config_loader::ConfigLayerStackOrdering;
|
||||
use crate::config_loader::ConfigRequirements;
|
||||
use crate::config_loader::ConfigRequirementsToml;
|
||||
use crate::config_loader::LoaderOverrides;
|
||||
use crate::config_loader::RequirementSource;
|
||||
use crate::config_loader::Sourced;
|
||||
use codex_app_server_protocol::ConfigLayerSource;
|
||||
use codex_config::RequirementsExecPolicy;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
@@ -17,6 +24,7 @@ use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tempfile::TempDir;
|
||||
use tempfile::tempdir;
|
||||
use toml::Value as TomlValue;
|
||||
|
||||
@@ -73,6 +81,92 @@ fn unrestricted_file_system_sandbox_policy() -> FileSystemSandboxPolicy {
|
||||
FileSystemSandboxPolicy::unrestricted()
|
||||
}
|
||||
|
||||
async fn test_config() -> (TempDir, Config) {
|
||||
let home = TempDir::new().expect("create temp dir");
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(home.path().to_path_buf())
|
||||
.loader_overrides(LoaderOverrides {
|
||||
#[cfg(target_os = "macos")]
|
||||
managed_preferences_base64: Some(String::new()),
|
||||
macos_managed_config_requirements_base64: Some(String::new()),
|
||||
..LoaderOverrides::default()
|
||||
})
|
||||
.build()
|
||||
.await
|
||||
.expect("load default test config");
|
||||
(home, config)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn child_uses_parent_exec_policy_when_layer_stack_matches() {
|
||||
let (_home, parent_config) = test_config().await;
|
||||
let child_config = parent_config.clone();
|
||||
|
||||
assert!(child_uses_parent_exec_policy(&parent_config, &child_config));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn child_uses_parent_exec_policy_when_non_exec_policy_layers_differ() {
|
||||
let (_home, parent_config) = test_config().await;
|
||||
let mut child_config = parent_config.clone();
|
||||
let mut layers: Vec<_> = child_config
|
||||
.config_layer_stack
|
||||
.get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
layers.push(ConfigLayerEntry::new(
|
||||
ConfigLayerSource::SessionFlags,
|
||||
TomlValue::Table(Default::default()),
|
||||
));
|
||||
child_config.config_layer_stack = ConfigLayerStack::new(
|
||||
layers,
|
||||
child_config.config_layer_stack.requirements().clone(),
|
||||
child_config.config_layer_stack.requirements_toml().clone(),
|
||||
)
|
||||
.expect("config layer stack");
|
||||
|
||||
assert!(child_uses_parent_exec_policy(&parent_config, &child_config));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn child_does_not_use_parent_exec_policy_when_requirements_exec_policy_differs() {
|
||||
let (_home, parent_config) = test_config().await;
|
||||
let mut child_config = parent_config.clone();
|
||||
let mut requirements = ConfigRequirements {
|
||||
exec_policy: child_config
|
||||
.config_layer_stack
|
||||
.requirements()
|
||||
.exec_policy
|
||||
.clone(),
|
||||
..ConfigRequirements::default()
|
||||
};
|
||||
let mut policy = Policy::empty();
|
||||
policy
|
||||
.add_prefix_rule(&["rm".to_string()], Decision::Forbidden)
|
||||
.expect("add prefix rule");
|
||||
requirements.exec_policy = Some(Sourced::new(
|
||||
RequirementsExecPolicy::new(policy),
|
||||
RequirementSource::Unknown,
|
||||
));
|
||||
child_config.config_layer_stack = ConfigLayerStack::new(
|
||||
child_config
|
||||
.config_layer_stack
|
||||
.get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
requirements,
|
||||
child_config.config_layer_stack.requirements_toml().clone(),
|
||||
)
|
||||
.expect("config layer stack");
|
||||
|
||||
assert!(!child_uses_parent_exec_policy(
|
||||
&parent_config,
|
||||
&child_config
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn returns_empty_policy_when_no_policy_files_exist() {
|
||||
let temp_dir = tempdir().expect("create temp dir");
|
||||
|
||||
Reference in New Issue
Block a user