mirror of
https://github.com/openai/codex.git
synced 2026-10-25 18:28:38 +00:00
permissions: start using PermissionProfile as the canonical runtime model
This commit is contained in:
@@ -1456,6 +1456,12 @@ impl Session {
|
||||
per_turn_config.service_tier = session_configuration.service_tier;
|
||||
per_turn_config.personality = session_configuration.personality;
|
||||
per_turn_config.approvals_reviewer = session_configuration.approvals_reviewer;
|
||||
per_turn_config.permissions.approval_policy = session_configuration.approval_policy.clone();
|
||||
per_turn_config.permissions.sandbox_policy = session_configuration.sandbox_policy.clone();
|
||||
per_turn_config.permissions.file_system_sandbox_policy =
|
||||
session_configuration.file_system_sandbox_policy.clone();
|
||||
per_turn_config.permissions.network_sandbox_policy =
|
||||
session_configuration.network_sandbox_policy;
|
||||
let resolved_web_search_mode = resolve_web_search_mode_for_turn(
|
||||
&per_turn_config.web_search_mode,
|
||||
session_configuration.sandbox_policy.get(),
|
||||
@@ -3557,6 +3563,11 @@ impl Session {
|
||||
ts.granted_permissions()
|
||||
}
|
||||
|
||||
pub(crate) async fn cwd(&self) -> AbsolutePathBuf {
|
||||
let state = self.state.lock().await;
|
||||
state.session_configuration.cwd.clone()
|
||||
}
|
||||
|
||||
pub(crate) async fn granted_session_permissions(&self) -> Option<PermissionProfile> {
|
||||
let state = self.state.lock().await;
|
||||
state.granted_permissions()
|
||||
|
||||
@@ -79,6 +79,10 @@ impl CodexThread {
|
||||
self.codex.shutdown_and_wait().await
|
||||
}
|
||||
|
||||
pub async fn cwd(&self) -> AbsolutePathBuf {
|
||||
self.codex.session.cwd().await
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub async fn ensure_rollout_materialized(&self) {
|
||||
self.codex.session.ensure_rollout_materialized().await;
|
||||
|
||||
@@ -48,6 +48,7 @@ use codex_model_provider_info::LMSTUDIO_OSS_PROVIDER_ID;
|
||||
use codex_model_provider_info::OLLAMA_OSS_PROVIDER_ID;
|
||||
use codex_model_provider_info::WireApi;
|
||||
use codex_models_manager::bundled_models_response;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
@@ -627,6 +628,13 @@ fn default_permissions_profile_populates_runtime_sandbox_policy() -> std::io::Re
|
||||
},
|
||||
]),
|
||||
);
|
||||
assert_eq!(
|
||||
config.permissions.runtime_permission_profile(),
|
||||
PermissionProfile::from_runtime_permissions(
|
||||
&config.permissions.file_system_sandbox_policy,
|
||||
config.permissions.network_sandbox_policy,
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
config.permissions.sandbox_policy.get(),
|
||||
&SandboxPolicy::WorkspaceWrite {
|
||||
@@ -1245,6 +1253,14 @@ exclude_slash_tmp = true
|
||||
NetworkSandboxPolicy::from(sandbox_policy),
|
||||
"case `{name}` should preserve network semantics from legacy config"
|
||||
);
|
||||
assert_eq!(
|
||||
config.permissions.runtime_permission_profile(),
|
||||
PermissionProfile::from_runtime_permissions(
|
||||
&config.permissions.file_system_sandbox_policy,
|
||||
config.permissions.network_sandbox_policy,
|
||||
),
|
||||
"case `{name}` should populate canonical permission profile from runtime policies"
|
||||
);
|
||||
assert_eq!(
|
||||
config
|
||||
.permissions
|
||||
|
||||
@@ -73,6 +73,8 @@ use codex_protocol::config_types::Verbosity;
|
||||
use codex_protocol::config_types::WebSearchConfig;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ModelsResponse;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
@@ -212,6 +214,16 @@ pub struct Permissions {
|
||||
pub windows_sandbox_private_desktop: bool,
|
||||
}
|
||||
|
||||
impl Permissions {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn runtime_permission_profile(&self) -> PermissionProfile {
|
||||
PermissionProfile::from_runtime_permissions(
|
||||
&self.file_system_sandbox_policy,
|
||||
self.network_sandbox_policy,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Application configuration loaded from disk and merged with overrides.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Config {
|
||||
|
||||
@@ -82,10 +82,10 @@ fn write_permissions_for_paths(
|
||||
.ok()?;
|
||||
|
||||
let permissions = (!write_paths.is_empty()).then_some(PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(write_paths),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(write_paths),
|
||||
)),
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
|
||||
@@ -85,7 +85,13 @@ fn write_permissions_for_paths_keep_dirs_outside_workspace_root() {
|
||||
dunce::simplified(&outside.canonicalize().expect("canonicalize outside dir")).abs();
|
||||
|
||||
assert_eq!(
|
||||
permissions.and_then(|profile| profile.file_system.and_then(|fs| fs.write)),
|
||||
Some(vec![expected_outside])
|
||||
permissions,
|
||||
Some(PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![expected_outside]),
|
||||
)),
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ mod tool_suggest;
|
||||
pub(crate) mod unified_exec;
|
||||
mod view_image;
|
||||
|
||||
use codex_sandboxing::policy_transforms::intersect_permission_profiles;
|
||||
use codex_sandboxing::policy_transforms::merge_permission_profiles;
|
||||
use codex_sandboxing::policy_transforms::normalize_additional_permissions;
|
||||
use codex_sandboxing::policy_transforms::permission_profile_is_preapproved;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_absolute_path::AbsolutePathBufGuard;
|
||||
use serde::Deserialize;
|
||||
@@ -183,17 +183,12 @@ pub(super) async fn apply_granted_turn_permissions(
|
||||
granted_session_permissions.as_ref(),
|
||||
granted_turn_permissions.as_ref(),
|
||||
);
|
||||
let effective_permissions = merge_permission_profiles(
|
||||
let session_cwd = session.cwd().await;
|
||||
let (effective_permissions, permissions_preapproved) = effective_permissions_for_granted_turn(
|
||||
additional_permissions.as_ref(),
|
||||
granted_permissions.as_ref(),
|
||||
session_cwd.as_path(),
|
||||
);
|
||||
let permissions_preapproved = match (effective_permissions.as_ref(), granted_permissions) {
|
||||
(Some(effective_permissions), Some(granted_permissions)) => {
|
||||
intersect_permission_profiles(effective_permissions.clone(), granted_permissions)
|
||||
== *effective_permissions
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let sandbox_permissions =
|
||||
if effective_permissions.is_some() && !sandbox_permissions.uses_additional_permissions() {
|
||||
@@ -209,9 +204,30 @@ pub(super) async fn apply_granted_turn_permissions(
|
||||
}
|
||||
}
|
||||
|
||||
fn effective_permissions_for_granted_turn(
|
||||
additional_permissions: Option<&PermissionProfile>,
|
||||
granted_permissions: Option<&PermissionProfile>,
|
||||
cwd: &Path,
|
||||
) -> (Option<PermissionProfile>, bool) {
|
||||
let effective_permissions =
|
||||
merge_permission_profiles(additional_permissions, granted_permissions);
|
||||
let permissions_preapproved = match (effective_permissions.as_ref(), granted_permissions) {
|
||||
(Some(effective_permissions), Some(granted_permissions)) => {
|
||||
permission_profile_is_preapproved(effective_permissions, granted_permissions, cwd)
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if permissions_preapproved {
|
||||
(granted_permissions.cloned(), true)
|
||||
} else {
|
||||
(effective_permissions, false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::EffectiveAdditionalPermissions;
|
||||
use super::effective_permissions_for_granted_turn;
|
||||
use super::implicit_granted_permissions;
|
||||
use super::normalize_and_validate_additional_permissions;
|
||||
use crate::sandboxing::SandboxPermissions;
|
||||
@@ -235,12 +251,24 @@ mod tests {
|
||||
|
||||
fn file_system_permissions(path: &std::path::Path) -> PermissionProfile {
|
||||
PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: None,
|
||||
write: Some(vec![
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
/*read*/ None,
|
||||
Some(vec![
|
||||
AbsolutePathBuf::from_absolute_path(path).expect("absolute path"),
|
||||
]),
|
||||
}),
|
||||
)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn read_file_system_permissions(path: &std::path::Path) -> PermissionProfile {
|
||||
PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![
|
||||
AbsolutePathBuf::from_absolute_path(path).expect("absolute path"),
|
||||
]),
|
||||
/*write*/ None,
|
||||
)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -322,4 +350,24 @@ mod tests {
|
||||
|
||||
assert_eq!(implicit_permissions, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preapproved_sticky_grants_do_not_get_narrowed_by_child_request() {
|
||||
let cwd = tempdir().expect("tempdir");
|
||||
let child_path = cwd.path().join("child");
|
||||
let requested_permissions = read_file_system_permissions(&child_path);
|
||||
let granted_permissions = file_system_permissions(cwd.path());
|
||||
|
||||
let (effective_permissions, permissions_preapproved) =
|
||||
effective_permissions_for_granted_turn(
|
||||
Some(&requested_permissions),
|
||||
Some(&granted_permissions),
|
||||
cwd.path(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
(effective_permissions, permissions_preapproved),
|
||||
(Some(granted_permissions), true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,10 +188,10 @@ fn exec_command_args_resolve_relative_additional_permissions_against_workdir() -
|
||||
assert_eq!(
|
||||
args.additional_permissions,
|
||||
Some(PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: None,
|
||||
write: Some(vec![expected_write.abs()]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
/*read*/ None,
|
||||
Some(vec![expected_write.abs()]),
|
||||
)),
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
|
||||
@@ -82,10 +82,10 @@ fn file_system_sandbox_context_uses_active_attempt() {
|
||||
.abs();
|
||||
let additional_permissions = PermissionProfile {
|
||||
network: None,
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![path.clone()]),
|
||||
write: Some(Vec::new()),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![path.clone()]),
|
||||
Some(Vec::new()),
|
||||
)),
|
||||
};
|
||||
let req = ApplyPatchRequest {
|
||||
action: ApplyPatchAction::new_add_for_test(&path, "hello".to_string()),
|
||||
|
||||
@@ -250,12 +250,12 @@ fn map_exec_result_preserves_stdout_and_stderr() {
|
||||
#[test]
|
||||
fn shell_request_escalation_execution_is_explicit() {
|
||||
let requested_permissions = PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: None,
|
||||
write: Some(vec![
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
/*read*/ None,
|
||||
Some(vec![
|
||||
AbsolutePathBuf::from_absolute_path("/tmp/output").unwrap(),
|
||||
]),
|
||||
}),
|
||||
)),
|
||||
..Default::default()
|
||||
};
|
||||
let sandbox_policy = SandboxPolicy::WorkspaceWrite {
|
||||
|
||||
@@ -7,6 +7,7 @@ use codex_features::Feature;
|
||||
use codex_protocol::config_types::ApprovalsReviewer;
|
||||
use codex_protocol::models::FileSystemPermissions;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::ExecApprovalRequestEvent;
|
||||
@@ -293,20 +294,20 @@ fn workspace_write_excluding_tmp() -> SandboxPolicy {
|
||||
|
||||
fn requested_directory_write_permissions(path: &Path) -> RequestPermissionProfile {
|
||||
RequestPermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![absolute_path(path)]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![absolute_path(path)]),
|
||||
)),
|
||||
..RequestPermissionProfile::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn normalized_directory_write_permissions(path: &Path) -> Result<RequestPermissionProfile> {
|
||||
Ok(RequestPermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![AbsolutePathBuf::try_from(path.canonicalize()?)?]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![AbsolutePathBuf::try_from(path.canonicalize()?)?]),
|
||||
)),
|
||||
..RequestPermissionProfile::default()
|
||||
})
|
||||
}
|
||||
@@ -343,10 +344,10 @@ async fn with_additional_permissions_requires_approval_under_on_request() -> Res
|
||||
let call_id = "request_permissions_skip_approval";
|
||||
let command = "touch requested-dir/requested-but-unused.txt";
|
||||
let requested_permissions = PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![absolute_path(&requested_dir_canonical)]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![absolute_path(&requested_dir_canonical)]),
|
||||
)),
|
||||
..Default::default()
|
||||
};
|
||||
let event = shell_event_with_request_permissions(call_id, command, &requested_permissions)?;
|
||||
@@ -521,10 +522,10 @@ async fn relative_additional_permissions_resolve_against_tool_workdir() -> Resul
|
||||
let call_id = "request_permissions_relative_workdir";
|
||||
let command = "touch relative-write.txt";
|
||||
let expected_permissions = PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: None,
|
||||
write: Some(vec![absolute_path(&nested_dir_canonical)]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
/*read*/ None,
|
||||
Some(vec![absolute_path(&nested_dir_canonical)]),
|
||||
)),
|
||||
..Default::default()
|
||||
};
|
||||
let event = shell_event_with_raw_request_permissions(
|
||||
@@ -624,10 +625,10 @@ async fn read_only_with_additional_permissions_does_not_widen_to_unrequested_cwd
|
||||
"cwd-widened", unrequested_write, unrequested_write
|
||||
);
|
||||
let requested_permissions = PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![absolute_path(&requested_write)]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![absolute_path(&requested_write)]),
|
||||
)),
|
||||
..Default::default()
|
||||
};
|
||||
let event = shell_event_with_request_permissions(call_id, &command, &requested_permissions)?;
|
||||
@@ -725,10 +726,10 @@ async fn read_only_with_additional_permissions_does_not_widen_to_unrequested_tmp
|
||||
"tmp-widened", tmp_write, tmp_write
|
||||
);
|
||||
let requested_permissions = PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![absolute_path(&requested_write)]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![absolute_path(&requested_write)]),
|
||||
)),
|
||||
..Default::default()
|
||||
};
|
||||
let event = shell_event_with_request_permissions(call_id, &command, &requested_permissions)?;
|
||||
@@ -824,19 +825,19 @@ async fn workspace_write_with_additional_permissions_can_write_outside_cwd() ->
|
||||
"outside-cwd-ok", outside_write, outside_write
|
||||
);
|
||||
let requested_permissions = RequestPermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![absolute_path(outside_dir.path())]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![absolute_path(outside_dir.path())]),
|
||||
)),
|
||||
..RequestPermissionProfile::default()
|
||||
};
|
||||
let normalized_requested_permissions = RequestPermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![AbsolutePathBuf::try_from(
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![AbsolutePathBuf::try_from(
|
||||
outside_dir.path().canonicalize()?,
|
||||
)?]),
|
||||
}),
|
||||
)),
|
||||
..RequestPermissionProfile::default()
|
||||
};
|
||||
let event = shell_event_with_request_permissions(call_id, &command, &requested_permissions)?;
|
||||
@@ -926,19 +927,19 @@ async fn with_additional_permissions_denied_approval_blocks_execution() -> Resul
|
||||
"should-not-write", outside_write, outside_write
|
||||
);
|
||||
let requested_permissions = PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![absolute_path(outside_dir.path())]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![absolute_path(outside_dir.path())]),
|
||||
)),
|
||||
..Default::default()
|
||||
};
|
||||
let normalized_requested_permissions = PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![AbsolutePathBuf::try_from(
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![AbsolutePathBuf::try_from(
|
||||
outside_dir.path().canonicalize()?,
|
||||
)?]),
|
||||
}),
|
||||
)),
|
||||
..Default::default()
|
||||
};
|
||||
let event = shell_event_with_request_permissions(call_id, &command, &requested_permissions)?;
|
||||
@@ -1028,19 +1029,19 @@ async fn request_permissions_grants_apply_to_later_exec_command_calls() -> Resul
|
||||
"sticky-grant-ok", outside_write, outside_write
|
||||
);
|
||||
let requested_permissions = RequestPermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![absolute_path(outside_dir.path())]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![absolute_path(outside_dir.path())]),
|
||||
)),
|
||||
..Default::default()
|
||||
};
|
||||
let normalized_requested_permissions = RequestPermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![AbsolutePathBuf::try_from(
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![AbsolutePathBuf::try_from(
|
||||
outside_dir.path().canonicalize()?,
|
||||
)?]),
|
||||
}),
|
||||
)),
|
||||
..Default::default()
|
||||
};
|
||||
let responses = mount_sse_sequence(
|
||||
@@ -1492,35 +1493,35 @@ async fn partial_request_permissions_grants_do_not_preapprove_new_permissions()
|
||||
);
|
||||
|
||||
let requested_permissions = RequestPermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![
|
||||
absolute_path(first_dir.path()),
|
||||
absolute_path(second_dir.path()),
|
||||
]),
|
||||
}),
|
||||
)),
|
||||
..RequestPermissionProfile::default()
|
||||
};
|
||||
let normalized_requested_permissions = RequestPermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![
|
||||
AbsolutePathBuf::try_from(first_dir.path().canonicalize()?)?,
|
||||
AbsolutePathBuf::try_from(second_dir.path().canonicalize()?)?,
|
||||
]),
|
||||
}),
|
||||
)),
|
||||
..RequestPermissionProfile::default()
|
||||
};
|
||||
let granted_permissions = normalized_directory_write_permissions(first_dir.path())?;
|
||||
let second_dir_permissions = requested_directory_write_permissions(second_dir.path());
|
||||
let merged_permissions = PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![
|
||||
AbsolutePathBuf::try_from(first_dir.path().canonicalize()?)?,
|
||||
AbsolutePathBuf::try_from(second_dir.path().canonicalize()?)?,
|
||||
]),
|
||||
}),
|
||||
)),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -1584,16 +1585,31 @@ async fn partial_request_permissions_grants_do_not_preapprove_new_permissions()
|
||||
let approval_file_system = approval_permissions
|
||||
.file_system
|
||||
.unwrap_or_else(|| panic!("expected filesystem permissions"));
|
||||
assert!(approval_file_system.read.as_ref().is_none_or(Vec::is_empty));
|
||||
|
||||
let mut approval_writes = approval_file_system.write.unwrap_or_default();
|
||||
assert_eq!(
|
||||
approval_file_system
|
||||
.explicit_path_entries()
|
||||
.filter(|(_, access)| *access == FileSystemAccessMode::Read)
|
||||
.count(),
|
||||
0
|
||||
);
|
||||
|
||||
let mut approval_writes = approval_file_system
|
||||
.explicit_path_entries()
|
||||
.filter_map(|(path, access)| {
|
||||
(access == FileSystemAccessMode::Write).then_some(path.clone())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
approval_writes.sort_by_key(|path| path.display().to_string());
|
||||
|
||||
let mut expected_writes = merged_permissions
|
||||
.file_system
|
||||
.unwrap_or_else(|| panic!("expected merged filesystem permissions"))
|
||||
.write
|
||||
.unwrap_or_default();
|
||||
.explicit_path_entries()
|
||||
.filter_map(|(path, access)| {
|
||||
(access == FileSystemAccessMode::Write).then_some(path.clone())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
expected_writes.sort_by_key(|path| path.display().to_string());
|
||||
|
||||
assert_eq!(approval_writes, expected_writes);
|
||||
|
||||
@@ -81,20 +81,20 @@ fn workspace_write_excluding_tmp() -> SandboxPolicy {
|
||||
|
||||
fn requested_directory_write_permissions(path: &Path) -> RequestPermissionProfile {
|
||||
RequestPermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![absolute_path(path)]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![absolute_path(path)]),
|
||||
)),
|
||||
..RequestPermissionProfile::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn normalized_directory_write_permissions(path: &Path) -> Result<RequestPermissionProfile> {
|
||||
Ok(RequestPermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
read: Some(vec![]),
|
||||
write: Some(vec![AbsolutePathBuf::try_from(path.canonicalize()?)?]),
|
||||
}),
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
Some(vec![]),
|
||||
Some(vec![AbsolutePathBuf::try_from(path.canonicalize()?)?]),
|
||||
)),
|
||||
..RequestPermissionProfile::default()
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user