mirror of
https://github.com/openai/codex.git
synced 2026-02-28 19:53:48 +00:00
Compare commits
1 Commits
main
...
dev/cc/cle
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d1f0e2a87 |
@@ -437,7 +437,6 @@ mod tests {
|
||||
dependencies: Some(SkillDependencies { tools }),
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: PathBuf::from("skill"),
|
||||
scope: SkillScope::User,
|
||||
}
|
||||
|
||||
@@ -483,7 +483,6 @@ mod tests {
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: PathBuf::from(path),
|
||||
scope: codex_protocol::protocol::SkillScope::User,
|
||||
}
|
||||
|
||||
@@ -253,7 +253,6 @@ mod tests {
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: skill_doc_path,
|
||||
scope: codex_protocol::protocol::SkillScope::User,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::config::Config;
|
||||
use crate::config::Permissions;
|
||||
use crate::config_loader::ConfigLayerStack;
|
||||
use crate::config_loader::ConfigLayerStackOrdering;
|
||||
use crate::config_loader::default_project_root_markers;
|
||||
@@ -12,7 +11,6 @@ use crate::skills::model::SkillLoadOutcome;
|
||||
use crate::skills::model::SkillMetadata;
|
||||
use crate::skills::model::SkillPolicy;
|
||||
use crate::skills::model::SkillToolDependency;
|
||||
use crate::skills::permissions::compile_permission_profile;
|
||||
use crate::skills::system::system_cache_root_dir;
|
||||
use codex_app_server_protocol::ConfigLayerSource;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
@@ -64,7 +62,6 @@ struct LoadedSkillMetadata {
|
||||
dependencies: Option<SkillDependencies>,
|
||||
policy: Option<SkillPolicy>,
|
||||
permission_profile: Option<PermissionProfile>,
|
||||
permissions: Option<Permissions>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
@@ -521,7 +518,6 @@ fn parse_skill_file(path: &Path, scope: SkillScope) -> Result<SkillMetadata, Ski
|
||||
dependencies,
|
||||
policy,
|
||||
permission_profile,
|
||||
permissions,
|
||||
} = load_skill_metadata(path);
|
||||
|
||||
validate_len(&name, MAX_NAME_LEN, "name")?;
|
||||
@@ -544,7 +540,6 @@ fn parse_skill_file(path: &Path, scope: SkillScope) -> Result<SkillMetadata, Ski
|
||||
dependencies,
|
||||
policy,
|
||||
permission_profile,
|
||||
permissions,
|
||||
path_to_skills_md: resolved_path,
|
||||
scope,
|
||||
})
|
||||
@@ -595,14 +590,11 @@ fn load_skill_metadata(skill_path: &Path) -> LoadedSkillMetadata {
|
||||
policy,
|
||||
permissions,
|
||||
} = parsed;
|
||||
let permission_profile = permissions.clone().filter(|profile| !profile.is_empty());
|
||||
|
||||
LoadedSkillMetadata {
|
||||
interface: resolve_interface(interface, skill_dir),
|
||||
dependencies: resolve_dependencies(dependencies),
|
||||
policy: resolve_policy(policy),
|
||||
permission_profile,
|
||||
permissions: compile_permission_profile(skill_dir, permissions),
|
||||
permission_profile: permissions.filter(|profile| !profile.is_empty()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,9 +827,7 @@ mod tests {
|
||||
use crate::config::ConfigBuilder;
|
||||
use crate::config::ConfigOverrides;
|
||||
use crate::config::ConfigToml;
|
||||
use crate::config::Constrained;
|
||||
use crate::config::ProjectConfig;
|
||||
use crate::config::types::ShellEnvironmentPolicy;
|
||||
use crate::config_loader::ConfigLayerEntry;
|
||||
use crate::config_loader::ConfigLayerStack;
|
||||
use crate::config_loader::ConfigRequirements;
|
||||
@@ -1066,7 +1056,6 @@ mod tests {
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -1215,7 +1204,6 @@ mod tests {
|
||||
}),
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -1272,7 +1260,6 @@ interface:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(skill_path.as_path()),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -1392,44 +1379,6 @@ permissions:
|
||||
macos: None,
|
||||
})
|
||||
);
|
||||
#[cfg(target_os = "macos")]
|
||||
let macos_seatbelt_profile_extensions =
|
||||
Some(crate::seatbelt_permissions::MacOsSeatbeltProfileExtensions::default());
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let macos_seatbelt_profile_extensions = None;
|
||||
assert_eq!(
|
||||
outcome.skills[0].permissions,
|
||||
Some(Permissions {
|
||||
approval_policy: Constrained::allow_any(crate::protocol::AskForApproval::Never),
|
||||
sandbox_policy: Constrained::allow_any(
|
||||
crate::protocol::SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![
|
||||
AbsolutePathBuf::try_from(normalized(
|
||||
skill_dir.join("output").as_path(),
|
||||
))
|
||||
.expect("absolute output path")
|
||||
],
|
||||
read_only_access: crate::protocol::ReadOnlyAccess::Restricted {
|
||||
include_platform_defaults: true,
|
||||
readable_roots: vec![
|
||||
AbsolutePathBuf::try_from(normalized(
|
||||
skill_dir.join("data").as_path(),
|
||||
))
|
||||
.expect("absolute data path")
|
||||
],
|
||||
},
|
||||
network_access: true,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
}
|
||||
),
|
||||
network: None,
|
||||
allow_login_shell: true,
|
||||
shell_environment_policy: ShellEnvironmentPolicy::default(),
|
||||
windows_sandbox_mode: None,
|
||||
macos_seatbelt_profile_extensions,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1454,34 +1403,7 @@ permissions: {}
|
||||
outcome.errors
|
||||
);
|
||||
assert_eq!(outcome.skills.len(), 1);
|
||||
#[cfg(target_os = "macos")]
|
||||
let expected = Some(Permissions {
|
||||
approval_policy: Constrained::allow_any(crate::protocol::AskForApproval::Never),
|
||||
sandbox_policy: Constrained::allow_any(
|
||||
crate::protocol::SandboxPolicy::new_read_only_policy(),
|
||||
),
|
||||
network: None,
|
||||
allow_login_shell: true,
|
||||
shell_environment_policy: ShellEnvironmentPolicy::default(),
|
||||
windows_sandbox_mode: None,
|
||||
macos_seatbelt_profile_extensions: Some(
|
||||
crate::seatbelt_permissions::MacOsSeatbeltProfileExtensions::default(),
|
||||
),
|
||||
});
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let expected = Some(Permissions {
|
||||
approval_policy: Constrained::allow_any(crate::protocol::AskForApproval::Never),
|
||||
sandbox_policy: Constrained::allow_any(
|
||||
crate::protocol::SandboxPolicy::new_read_only_policy(),
|
||||
),
|
||||
network: None,
|
||||
allow_login_shell: true,
|
||||
shell_environment_policy: ShellEnvironmentPolicy::default(),
|
||||
windows_sandbox_mode: None,
|
||||
macos_seatbelt_profile_extensions: None,
|
||||
});
|
||||
assert_eq!(outcome.skills[0].permission_profile, None);
|
||||
assert_eq!(outcome.skills[0].permissions, expected);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -1513,24 +1435,21 @@ permissions:
|
||||
outcome.errors
|
||||
);
|
||||
assert_eq!(outcome.skills.len(), 1);
|
||||
let profile = outcome.skills[0]
|
||||
.permissions
|
||||
.as_ref()
|
||||
.expect("permission profile");
|
||||
assert_eq!(
|
||||
profile.macos_seatbelt_profile_extensions,
|
||||
Some(
|
||||
crate::seatbelt_permissions::MacOsSeatbeltProfileExtensions {
|
||||
macos_preferences:
|
||||
crate::seatbelt_permissions::MacOsPreferencesPermission::ReadWrite,
|
||||
macos_automation:
|
||||
crate::seatbelt_permissions::MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string()
|
||||
],),
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
}
|
||||
)
|
||||
outcome.skills[0].permission_profile,
|
||||
Some(PermissionProfile {
|
||||
macos: Some(codex_protocol::models::MacOsPermissions {
|
||||
preferences: Some(codex_protocol::models::MacOsPreferencesValue::Mode(
|
||||
"readwrite".to_string(),
|
||||
),),
|
||||
automations: Some(codex_protocol::models::MacOsAutomationValue::BundleIds(
|
||||
vec!["com.apple.Notes".to_string()],
|
||||
)),
|
||||
accessibility: Some(true),
|
||||
calendar: Some(true),
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1564,17 +1483,19 @@ permissions:
|
||||
);
|
||||
assert_eq!(outcome.skills.len(), 1);
|
||||
assert_eq!(
|
||||
outcome.skills[0].permissions,
|
||||
Some(Permissions {
|
||||
approval_policy: Constrained::allow_any(crate::protocol::AskForApproval::Never),
|
||||
sandbox_policy: Constrained::allow_any(
|
||||
crate::protocol::SandboxPolicy::new_read_only_policy(),
|
||||
),
|
||||
network: None,
|
||||
allow_login_shell: true,
|
||||
shell_environment_policy: ShellEnvironmentPolicy::default(),
|
||||
windows_sandbox_mode: None,
|
||||
macos_seatbelt_profile_extensions: None,
|
||||
outcome.skills[0].permission_profile,
|
||||
Some(PermissionProfile {
|
||||
macos: Some(codex_protocol::models::MacOsPermissions {
|
||||
preferences: Some(codex_protocol::models::MacOsPreferencesValue::Mode(
|
||||
"readwrite".to_string(),
|
||||
)),
|
||||
automations: Some(codex_protocol::models::MacOsAutomationValue::BundleIds(
|
||||
vec!["com.apple.Notes".to_string()],
|
||||
)),
|
||||
accessibility: Some(true),
|
||||
calendar: Some(true),
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -1624,7 +1545,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -1666,7 +1586,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -1721,7 +1640,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -1764,7 +1682,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -1810,7 +1727,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&shared_skill_path),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -1872,7 +1788,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -1910,7 +1825,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&shared_skill_path),
|
||||
scope: SkillScope::Admin,
|
||||
}]
|
||||
@@ -1952,7 +1866,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&linked_skill_path),
|
||||
scope: SkillScope::Repo,
|
||||
}]
|
||||
@@ -2021,7 +1934,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&within_depth_path),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -2050,7 +1962,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -2083,7 +1994,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::User,
|
||||
}]
|
||||
@@ -2197,7 +2107,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::Repo,
|
||||
}]
|
||||
@@ -2234,7 +2143,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::Repo,
|
||||
}]
|
||||
@@ -2289,7 +2197,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&nested_skill_path),
|
||||
scope: SkillScope::Repo,
|
||||
},
|
||||
@@ -2301,7 +2208,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&root_skill_path),
|
||||
scope: SkillScope::Repo,
|
||||
},
|
||||
@@ -2342,7 +2248,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::Repo,
|
||||
}]
|
||||
@@ -2381,7 +2286,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::Repo,
|
||||
}]
|
||||
@@ -2424,7 +2328,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&repo_skill_path),
|
||||
scope: SkillScope::Repo,
|
||||
},
|
||||
@@ -2436,7 +2339,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&user_skill_path),
|
||||
scope: SkillScope::User,
|
||||
},
|
||||
@@ -2502,7 +2404,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: first_path,
|
||||
scope: SkillScope::Repo,
|
||||
},
|
||||
@@ -2514,7 +2415,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: second_path,
|
||||
scope: SkillScope::Repo,
|
||||
},
|
||||
@@ -2587,7 +2487,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::Repo,
|
||||
}]
|
||||
@@ -2647,7 +2546,6 @@ permissions:
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: normalized(&skill_path),
|
||||
scope: SkillScope::System,
|
||||
}]
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::config::Permissions;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::protocol::SkillScope;
|
||||
|
||||
@@ -16,8 +15,6 @@ pub struct SkillMetadata {
|
||||
pub dependencies: Option<SkillDependencies>,
|
||||
pub policy: Option<SkillPolicy>,
|
||||
pub permission_profile: Option<PermissionProfile>,
|
||||
// This is an experimental field.
|
||||
pub permissions: Option<Permissions>,
|
||||
/// Path to the SKILLS.md file that declares this skill.
|
||||
pub path_to_skills_md: PathBuf,
|
||||
pub scope: SkillScope,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use codex_protocol::models::MacOsAutomationValue;
|
||||
@@ -20,7 +19,6 @@ use crate::protocol::ReadOnlyAccess;
|
||||
use crate::protocol::SandboxPolicy;
|
||||
|
||||
pub(crate) fn compile_permission_profile(
|
||||
_skill_dir: &Path,
|
||||
permissions: Option<PermissionProfile>,
|
||||
) -> Option<Permissions> {
|
||||
let PermissionProfile {
|
||||
@@ -225,21 +223,18 @@ mod tests {
|
||||
let read_dir = skill_dir.join("data");
|
||||
fs::create_dir_all(&read_dir).expect("read dir");
|
||||
|
||||
let profile = compile_permission_profile(
|
||||
&skill_dir,
|
||||
Some(PermissionProfile {
|
||||
network: Some(true),
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![
|
||||
absolute_path(&skill_dir.join("data")),
|
||||
absolute_path(&skill_dir.join("data")),
|
||||
absolute_path(&skill_dir.join("scripts/../data")),
|
||||
]),
|
||||
write: Some(vec![absolute_path(&skill_dir.join("output"))]),
|
||||
}),
|
||||
..Default::default()
|
||||
let profile = compile_permission_profile(Some(PermissionProfile {
|
||||
network: Some(true),
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![
|
||||
absolute_path(&skill_dir.join("data")),
|
||||
absolute_path(&skill_dir.join("data")),
|
||||
absolute_path(&skill_dir.join("scripts/../data")),
|
||||
]),
|
||||
write: Some(vec![absolute_path(&skill_dir.join("output"))]),
|
||||
}),
|
||||
)
|
||||
..Default::default()
|
||||
}))
|
||||
.expect("profile");
|
||||
|
||||
assert_eq!(
|
||||
@@ -284,7 +279,7 @@ mod tests {
|
||||
let skill_dir = tempdir.path().join("skill");
|
||||
fs::create_dir_all(&skill_dir).expect("skill dir");
|
||||
|
||||
let profile = compile_permission_profile(&skill_dir, None);
|
||||
let profile = compile_permission_profile(None);
|
||||
|
||||
assert_eq!(profile, None);
|
||||
}
|
||||
@@ -295,13 +290,10 @@ mod tests {
|
||||
let skill_dir = tempdir.path().join("skill");
|
||||
fs::create_dir_all(&skill_dir).expect("skill dir");
|
||||
|
||||
let profile = compile_permission_profile(
|
||||
&skill_dir,
|
||||
Some(PermissionProfile {
|
||||
network: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
let profile = compile_permission_profile(Some(PermissionProfile {
|
||||
network: Some(true),
|
||||
..Default::default()
|
||||
}))
|
||||
.expect("profile");
|
||||
|
||||
assert_eq!(
|
||||
@@ -330,17 +322,14 @@ mod tests {
|
||||
let read_dir = skill_dir.join("data");
|
||||
fs::create_dir_all(&read_dir).expect("read dir");
|
||||
|
||||
let profile = compile_permission_profile(
|
||||
&skill_dir,
|
||||
Some(PermissionProfile {
|
||||
network: Some(true),
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![absolute_path(&skill_dir.join("data"))]),
|
||||
write: Some(Vec::new()),
|
||||
}),
|
||||
..Default::default()
|
||||
let profile = compile_permission_profile(Some(PermissionProfile {
|
||||
network: Some(true),
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![absolute_path(&skill_dir.join("data"))]),
|
||||
write: Some(Vec::new()),
|
||||
}),
|
||||
)
|
||||
..Default::default()
|
||||
}))
|
||||
.expect("profile");
|
||||
|
||||
assert_eq!(
|
||||
@@ -379,20 +368,17 @@ mod tests {
|
||||
let skill_dir = tempdir.path().join("skill");
|
||||
fs::create_dir_all(&skill_dir).expect("skill dir");
|
||||
|
||||
let profile = compile_permission_profile(
|
||||
&skill_dir,
|
||||
Some(PermissionProfile {
|
||||
macos: Some(MacOsPermissions {
|
||||
preferences: Some(MacOsPreferencesValue::Mode("readwrite".to_string())),
|
||||
automations: Some(MacOsAutomationValue::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
])),
|
||||
accessibility: Some(true),
|
||||
calendar: Some(true),
|
||||
}),
|
||||
..Default::default()
|
||||
let profile = compile_permission_profile(Some(PermissionProfile {
|
||||
macos: Some(MacOsPermissions {
|
||||
preferences: Some(MacOsPreferencesValue::Mode("readwrite".to_string())),
|
||||
automations: Some(MacOsAutomationValue::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
])),
|
||||
accessibility: Some(true),
|
||||
calendar: Some(true),
|
||||
}),
|
||||
)
|
||||
..Default::default()
|
||||
}))
|
||||
.expect("profile");
|
||||
|
||||
assert_eq!(
|
||||
@@ -419,8 +405,8 @@ mod tests {
|
||||
let skill_dir = tempdir.path().join("skill");
|
||||
fs::create_dir_all(&skill_dir).expect("skill dir");
|
||||
|
||||
let profile = compile_permission_profile(&skill_dir, Some(PermissionProfile::default()))
|
||||
.expect("profile");
|
||||
let profile =
|
||||
compile_permission_profile(Some(PermissionProfile::default())).expect("profile");
|
||||
|
||||
assert_eq!(
|
||||
profile.macos_seatbelt_profile_extensions,
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::features::Feature;
|
||||
use crate::sandboxing::SandboxPermissions;
|
||||
use crate::shell::ShellType;
|
||||
use crate::skills::SkillMetadata;
|
||||
use crate::skills::permissions::compile_permission_profile;
|
||||
use crate::tools::runtimes::ExecveSessionApproval;
|
||||
use crate::tools::runtimes::build_command_spec;
|
||||
use crate::tools::sandboxing::SandboxAttempt;
|
||||
@@ -225,9 +226,7 @@ impl CoreShellActionProvider {
|
||||
}
|
||||
|
||||
fn skill_escalation_execution(skill: &SkillMetadata) -> EscalationExecution {
|
||||
skill
|
||||
.permissions
|
||||
.as_ref()
|
||||
compile_permission_profile(skill.permission_profile.clone())
|
||||
.map(|permissions| {
|
||||
EscalationExecution::Permissions(EscalationPermissions::Permissions(
|
||||
EscalatedPermissions {
|
||||
@@ -238,13 +237,6 @@ impl CoreShellActionProvider {
|
||||
},
|
||||
))
|
||||
})
|
||||
.or_else(|| {
|
||||
skill
|
||||
.permission_profile
|
||||
.clone()
|
||||
.map(EscalationPermissions::PermissionProfile)
|
||||
.map(EscalationExecution::Permissions)
|
||||
})
|
||||
.unwrap_or(EscalationExecution::TurnDefault)
|
||||
}
|
||||
|
||||
|
||||
@@ -514,6 +514,140 @@ async fn shell_zsh_fork_skill_without_permissions_inherits_turn_sandbox() -> Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Empty skill permissions should behave like no skill override and inherit the
|
||||
/// turn sandbox instead of forcing an explicit read-only skill sandbox.
|
||||
#[cfg(unix)]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn shell_zsh_fork_skill_with_empty_permissions_inherits_turn_sandbox() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let Some(runtime) = zsh_fork_runtime("zsh-fork empty skill permissions test")? else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let outside_dir = tempfile::tempdir_in(std::env::current_dir()?)?;
|
||||
let outside_path = outside_dir
|
||||
.path()
|
||||
.join("zsh-fork-skill-empty-permissions.txt");
|
||||
let outside_path_quoted = shlex::try_join([outside_path.to_string_lossy().as_ref()])?;
|
||||
let script_contents = format!(
|
||||
"#!/bin/sh\nprintf '%s' allowed > {outside_path_quoted}\ncat {outside_path_quoted}\n"
|
||||
);
|
||||
let outside_path_for_hook = outside_path.clone();
|
||||
let script_contents_for_hook = script_contents.clone();
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let test = build_zsh_fork_test(
|
||||
&server,
|
||||
runtime,
|
||||
AskForApproval::OnRequest,
|
||||
SandboxPolicy::DangerFullAccess,
|
||||
move |home| {
|
||||
let _ = fs::remove_file(&outside_path_for_hook);
|
||||
write_skill_with_shell_script_contents(
|
||||
home,
|
||||
"mbolin-test-skill",
|
||||
"sandboxed.sh",
|
||||
&script_contents_for_hook,
|
||||
)
|
||||
.unwrap();
|
||||
write_skill_metadata(home, "mbolin-test-skill", "permissions: {}\n").unwrap();
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
let (script_path_str, command) = skill_script_command(&test, "sandboxed.sh")?;
|
||||
|
||||
let first_call_id = "zsh-fork-skill-empty-permissions-1";
|
||||
let first_arguments = shell_command_arguments(&command)?;
|
||||
let first_mocks = mount_function_call_agent_response(
|
||||
&server,
|
||||
first_call_id,
|
||||
&first_arguments,
|
||||
"shell_command",
|
||||
)
|
||||
.await;
|
||||
|
||||
submit_turn_with_policies(
|
||||
&test,
|
||||
"use $mbolin-test-skill",
|
||||
AskForApproval::OnRequest,
|
||||
SandboxPolicy::DangerFullAccess,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let approval = wait_for_exec_approval_request(&test)
|
||||
.await
|
||||
.expect("expected exec approval request before completion");
|
||||
assert_eq!(approval.call_id, first_call_id);
|
||||
assert_eq!(approval.command, vec![script_path_str.clone()]);
|
||||
assert_eq!(approval.additional_permissions, None);
|
||||
|
||||
test.codex
|
||||
.submit(Op::ExecApproval {
|
||||
id: approval.effective_approval_id(),
|
||||
turn_id: None,
|
||||
decision: ReviewDecision::ApprovedForSession,
|
||||
})
|
||||
.await?;
|
||||
|
||||
wait_for_turn_complete(&test).await;
|
||||
|
||||
let first_output = first_mocks
|
||||
.completion
|
||||
.single_request()
|
||||
.function_call_output(first_call_id)["output"]
|
||||
.as_str()
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
assert!(
|
||||
first_output.contains("allowed"),
|
||||
"expected empty skill permissions to inherit full-access turn sandbox, got output: {first_output:?}"
|
||||
);
|
||||
assert_eq!(fs::read_to_string(&outside_path)?, "allowed");
|
||||
|
||||
let second_call_id = "zsh-fork-skill-empty-permissions-2";
|
||||
let second_arguments = shell_command_arguments(&command)?;
|
||||
let second_mocks = mount_function_call_agent_response(
|
||||
&server,
|
||||
second_call_id,
|
||||
&second_arguments,
|
||||
"shell_command",
|
||||
)
|
||||
.await;
|
||||
|
||||
let _ = fs::remove_file(&outside_path);
|
||||
|
||||
submit_turn_with_policies(
|
||||
&test,
|
||||
"use $mbolin-test-skill",
|
||||
AskForApproval::OnRequest,
|
||||
SandboxPolicy::DangerFullAccess,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let cached_approval = wait_for_exec_approval_request(&test).await;
|
||||
assert!(
|
||||
cached_approval.is_none(),
|
||||
"expected second run to reuse the cached session approval"
|
||||
);
|
||||
|
||||
let second_output = second_mocks
|
||||
.completion
|
||||
.single_request()
|
||||
.function_call_output(second_call_id)["output"]
|
||||
.as_str()
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
assert!(
|
||||
second_output.contains("allowed"),
|
||||
"expected cached empty-permissions skill approval to inherit the turn sandbox, got output: {second_output:?}"
|
||||
);
|
||||
assert_eq!(fs::read_to_string(&outside_path)?, "allowed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The validation to focus on is: writes to the skill-approved folder succeed,
|
||||
/// and writes to an unrelated folder fail, both before and after cached approval.
|
||||
#[cfg(unix)]
|
||||
|
||||
@@ -1522,7 +1522,6 @@ mod tests {
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: PathBuf::from("test-skill"),
|
||||
scope: SkillScope::User,
|
||||
}]),
|
||||
|
||||
@@ -191,7 +191,6 @@ fn protocol_skill_to_core(skill: &ProtocolSkillMetadata) -> SkillMetadata {
|
||||
}),
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: skill.path.clone(),
|
||||
scope: skill.scope,
|
||||
}
|
||||
|
||||
@@ -936,7 +936,6 @@ async fn submission_prefers_selected_duplicate_skill_path() {
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: repo_skill_path,
|
||||
scope: SkillScope::Repo,
|
||||
},
|
||||
@@ -948,7 +947,6 @@ async fn submission_prefers_selected_duplicate_skill_path() {
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
permissions: None,
|
||||
path_to_skills_md: user_skill_path.clone(),
|
||||
scope: SkillScope::User,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user