mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Render remote permission paths using executor context (#47867)
## What changed - Resolve remote sandbox modes, writable roots, and denied read paths/globs using the executor's filesystem context, displaying paths in its native format. Preserve local filesystem resolution for local environments. - Resolve remote read denials individually so missing executor facts omit only the unresolved entries. - Apply a shared 32 KiB budget to displayed permission paths/globs, omitting whole entries and warning that all restrictions still apply, including omitted read denials. ## Testing Add coverage for remote Windows writable roots and read denials, missing executor path facts, and whole-entry omission with multibyte paths under the shared budget. GitOrigin-RevId: c61e7ddc8fa569dbc25e9990d90357c42315188f
This commit is contained in:
@@ -10,6 +10,7 @@ use codex_execpolicy::Policy;
|
||||
use codex_prompts::ApprovalPromptContext;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::models::format_allow_prefixes;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicyContext;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
@@ -34,12 +35,14 @@ pub(crate) enum PermissionsSnapshot {
|
||||
}
|
||||
|
||||
impl PermissionsState {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
permission_profile: &PermissionProfile,
|
||||
approval_policy: AskForApproval,
|
||||
approval_context: ApprovalPromptContext<'_>,
|
||||
exec_policy: &Policy,
|
||||
cwd: &Path,
|
||||
paths: Option<&FileSystemSandboxPolicyContext<'_>>,
|
||||
exec_permission_approvals_enabled: bool,
|
||||
request_permissions_tool_enabled: bool,
|
||||
) -> Self {
|
||||
@@ -50,6 +53,7 @@ impl PermissionsState {
|
||||
approval_context,
|
||||
exec_policy,
|
||||
cwd,
|
||||
paths,
|
||||
exec_permission_approvals_enabled,
|
||||
request_permissions_tool_enabled,
|
||||
)
|
||||
|
||||
@@ -237,6 +237,7 @@ fn permissions_state(
|
||||
ApprovalPromptContext::new(ApprovalsReviewer::User, model_messages),
|
||||
&Policy::empty(),
|
||||
Path::new("/workspace"),
|
||||
/*paths*/ None,
|
||||
/*exec_permission_approvals_enabled*/ false,
|
||||
/*request_permissions_tool_enabled*/ false,
|
||||
)
|
||||
@@ -250,6 +251,7 @@ fn permissions_state_with_default_messages(exec_policy: &Policy) -> PermissionsS
|
||||
ApprovalPromptContext::new(ApprovalsReviewer::User, model_messages),
|
||||
exec_policy,
|
||||
Path::new("/workspace"),
|
||||
/*paths*/ None,
|
||||
/*exec_permission_approvals_enabled*/ false,
|
||||
/*request_permissions_tool_enabled*/ false,
|
||||
)
|
||||
|
||||
@@ -24,6 +24,7 @@ use crate::context::world_state::WorldState;
|
||||
use codex_connectors::AppToolPolicyEvaluator;
|
||||
use codex_extension_api::WorldStateContributionInput;
|
||||
use codex_features::Feature;
|
||||
use codex_file_system::FileSystemSandboxContext;
|
||||
use codex_prompts::ApprovalPromptContext;
|
||||
use codex_prompts::ResolvedModelMessages;
|
||||
use codex_prompts::render_model_instructions;
|
||||
@@ -166,6 +167,14 @@ impl Session {
|
||||
.current_for_prefix_rules(turn_context.allow_prefix_rules());
|
||||
if turn_context.config.include_permissions_instructions {
|
||||
let environment = step_context.environments.primary();
|
||||
let sandbox = environment
|
||||
.filter(|environment| environment.environment.is_remote())
|
||||
.map(|environment| {
|
||||
environment.sandbox_context(/*additional_permissions*/ None)
|
||||
});
|
||||
let paths = sandbox
|
||||
.as_ref()
|
||||
.map(FileSystemSandboxContext::policy_context);
|
||||
let permission_profile =
|
||||
turn_context.permission_profile_for_environments(&step_context.environments);
|
||||
#[allow(deprecated)]
|
||||
@@ -178,6 +187,7 @@ impl Session {
|
||||
ApprovalPromptContext::new(settings.approvals_reviewer(), model_messages),
|
||||
exec_policy.as_ref(),
|
||||
&cwd,
|
||||
paths.as_ref(),
|
||||
turn_context
|
||||
.config
|
||||
.features
|
||||
|
||||
@@ -729,6 +729,28 @@ timeout = 900
|
||||
)]),
|
||||
NetworkSandboxPolicy::Enabled,
|
||||
)
|
||||
} else if foreign_cwd
|
||||
&& matches!(
|
||||
scenario,
|
||||
PushedExecScenario::Complete | PushedExecScenario::ElevatedPowerShell
|
||||
)
|
||||
{
|
||||
let mut file_system = PermissionProfile::workspace_write().file_system_sandbox_policy();
|
||||
file_system.entries.extend([
|
||||
FileSystemSandboxEntry::new(
|
||||
FileSystemPath::Path {
|
||||
path: "file:///C:/workspace/blocked".parse()?,
|
||||
},
|
||||
FileSystemAccessMode::Deny,
|
||||
),
|
||||
FileSystemSandboxEntry::new(
|
||||
FileSystemPath::GlobPattern {
|
||||
pattern: r"C:\workspace\private\**".into(),
|
||||
},
|
||||
FileSystemAccessMode::Deny,
|
||||
),
|
||||
]);
|
||||
PermissionProfile::from_runtime_permissions(&file_system, NetworkSandboxPolicy::Restricted)
|
||||
} else if foreign_cwd && !matches!(scenario, PushedExecScenario::UnsandboxedInterceptedPatch) {
|
||||
PermissionProfile::workspace_write()
|
||||
} else {
|
||||
@@ -976,6 +998,28 @@ timeout = 900
|
||||
"toolCallId": CALL_ID,
|
||||
}),
|
||||
);
|
||||
if foreign_cwd && !managed_network_configured {
|
||||
let permissions = response_mock.requests()[0]
|
||||
.message_input_texts("developer")
|
||||
.join("\n")
|
||||
.lines()
|
||||
.filter(|line| {
|
||||
line.contains("`sandbox_mode`")
|
||||
|| line.starts_with(" The writable root")
|
||||
|| line.starts_with("- path `")
|
||||
|| line.starts_with("- glob `")
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
insta::allow_duplicates! {
|
||||
insta::assert_snapshot!(permissions, @r#"
|
||||
Filesystem sandboxing defines which files can be read or written. `sandbox_mode` is `workspace-write`: The sandbox permits reading files, and editing files in `cwd` and `writable_roots`. Editing files in other directories requires approval. Network access is restricted.
|
||||
The writable roots are `C:\workspace`, `D:\other-workspace`.
|
||||
- path `C:\workspace\blocked`
|
||||
- glob `C:\workspace\private\**`
|
||||
"#);
|
||||
}
|
||||
}
|
||||
if foreign_cwd {
|
||||
let params = &exec_server_result.process_start["params"];
|
||||
assert_eq!(params["cwd"], "file:///C:/workspace");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Composes selected permission text and prepared runtime facts into a stored fragment body.
|
||||
//! Only the profile adapter inspects the host filesystem; composition uses the supplied facts.
|
||||
//! Remote paths use executor context; the local adapter retains host filesystem resolution.
|
||||
//! Displayed paths share a byte budget and are omitted only at whole-entry boundaries.
|
||||
//! Catalog text uses literal substitution; bundled sandbox templates are parsed and cached here.
|
||||
|
||||
use crate::ResolvedMessage;
|
||||
@@ -16,10 +17,12 @@ use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::models::format_allow_prefixes;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicyContext;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::GranularApprovalConfig;
|
||||
use codex_protocol::protocol::NetworkAccess;
|
||||
use codex_utils_template::Template;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
@@ -34,6 +37,8 @@ const GRANULAR_PROMPTED_CATEGORIES: &str =
|
||||
"These approval categories may still prompt the user when needed:";
|
||||
const GRANULAR_REJECTED_CATEGORIES: &str =
|
||||
"These approval categories are automatically rejected instead of prompting the user:";
|
||||
const MAX_PERMISSION_PATH_BYTES: usize = 32 * 1024;
|
||||
const OMITTED_PERMISSION_PATHS: &str = "Additional permission paths/globs are omitted. All restrictions still apply; do not use escalation or additional permissions to bypass omitted read denials.";
|
||||
|
||||
static DANGER_FULL_ACCESS: LazyLock<Template> = LazyLock::new(|| {
|
||||
Template::parse(DANGER_FULL_ACCESS_TEMPLATE.trim_end())
|
||||
@@ -93,6 +98,28 @@ impl PermissionsInstructions {
|
||||
context: PermissionsRenderContext<'_>,
|
||||
approval_context: ApprovalPromptContext<'_>,
|
||||
) -> Self {
|
||||
let mut remaining_path_bytes =
|
||||
MAX_PERMISSION_PATH_BYTES - OMITTED_PERMISSION_PATHS.len() - 1;
|
||||
let mut paths_omitted = false;
|
||||
let mut bounded_paths = |paths: &[String]| {
|
||||
paths
|
||||
.iter()
|
||||
.filter(|path| {
|
||||
// Reserve up to ten bytes for each entry's label, quoting, and separator.
|
||||
if let Some(remaining) = remaining_path_bytes.checked_sub(path.len() + 10) {
|
||||
remaining_path_bytes = remaining;
|
||||
true
|
||||
} else {
|
||||
paths_omitted = true;
|
||||
false
|
||||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let writable_roots = bounded_paths(context.writable_roots);
|
||||
let denied_read_paths = bounded_paths(context.denied_read_paths);
|
||||
let denied_read_globs = bounded_paths(context.denied_read_globs);
|
||||
let mut text = String::new();
|
||||
let sandbox = sandbox_text(
|
||||
context.sandbox_mode,
|
||||
@@ -113,54 +140,100 @@ impl PermissionsInstructions {
|
||||
context.request_permissions_tool_enabled,
|
||||
),
|
||||
);
|
||||
if let Some(writable_roots) = writable_roots_text(context.writable_roots) {
|
||||
if let Some(writable_roots) = writable_roots_text(&writable_roots) {
|
||||
append_section(&mut text, &writable_roots);
|
||||
}
|
||||
if let Some(denied_reads) =
|
||||
denied_reads_text(context.denied_read_paths, context.denied_read_globs)
|
||||
{
|
||||
if let Some(denied_reads) = denied_reads_text(&denied_read_paths, &denied_read_globs) {
|
||||
append_section(&mut text, &denied_reads);
|
||||
}
|
||||
if paths_omitted {
|
||||
append_section(&mut text, OMITTED_PERMISSION_PATHS);
|
||||
}
|
||||
if !text.ends_with('\n') {
|
||||
text.push('\n');
|
||||
}
|
||||
Self { text }
|
||||
}
|
||||
|
||||
/// Resolves a permission profile against the host filesystem before rendering instructions.
|
||||
/// Uses executor paths when supplied, otherwise retaining local path resolution.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn from_permission_profile(
|
||||
permission_profile: &PermissionProfile,
|
||||
approval_policy: AskForApproval,
|
||||
approval_context: ApprovalPromptContext<'_>,
|
||||
exec_policy: &Policy,
|
||||
cwd: &Path,
|
||||
paths: Option<&FileSystemSandboxPolicyContext<'_>>,
|
||||
exec_permission_approvals_enabled: bool,
|
||||
request_permissions_tool_enabled: bool,
|
||||
) -> Self {
|
||||
let file_system_policy = permission_profile.file_system_sandbox_policy();
|
||||
let (sandbox_mode, mut writable_roots) = if file_system_policy.has_full_disk_write_access()
|
||||
{
|
||||
(SandboxMode::DangerFullAccess, Vec::new())
|
||||
let full_disk_write = paths.map_or_else(
|
||||
|| file_system_policy.has_full_disk_write_access(),
|
||||
|paths| file_system_policy.has_full_disk_write_access_with_context(paths),
|
||||
);
|
||||
let (writable_roots, denied_read_paths, denied_read_globs) = if let Some(paths) = paths {
|
||||
let mut writable_roots = file_system_policy
|
||||
.resolved_entries(paths)
|
||||
.into_iter()
|
||||
.filter(|(path, access)| {
|
||||
!full_disk_write
|
||||
&& access.can_write()
|
||||
&& file_system_policy.can_write_path(path, paths)
|
||||
})
|
||||
.map(|(path, _)| path)
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
.map(|path| path.inferred_native_path_string())
|
||||
.collect::<Vec<_>>();
|
||||
writable_roots.sort();
|
||||
let mut denied_read_paths = Vec::new();
|
||||
let mut denied_read_globs = Vec::new();
|
||||
let mut seen = HashSet::new();
|
||||
let mut entry_policy = file_system_policy.clone();
|
||||
// Missing executor facts should omit only the unresolved entry from the text.
|
||||
for entry in &file_system_policy.entries {
|
||||
entry_policy.entries = vec![entry.clone()];
|
||||
denied_read_paths.extend(
|
||||
entry_policy
|
||||
.get_unreadable_roots_with_context(paths)
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter(|path| seen.insert(path.clone()))
|
||||
.map(|path| path.inferred_native_path_string()),
|
||||
);
|
||||
denied_read_globs.extend(
|
||||
entry_policy
|
||||
.get_unreadable_globs_with_context(paths)
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
denied_read_globs.sort();
|
||||
denied_read_globs.dedup();
|
||||
(writable_roots, denied_read_paths, denied_read_globs)
|
||||
} else {
|
||||
let roots = file_system_policy.get_writable_roots_with_cwd(cwd);
|
||||
let mode = if roots.is_empty() {
|
||||
SandboxMode::ReadOnly
|
||||
} else {
|
||||
SandboxMode::WorkspaceWrite
|
||||
};
|
||||
(mode, roots)
|
||||
let mut writable_roots = file_system_policy.get_writable_roots_with_cwd(cwd);
|
||||
writable_roots.sort_by(|left, right| left.root.as_path().cmp(right.root.as_path()));
|
||||
(
|
||||
writable_roots
|
||||
.iter()
|
||||
.map(|root| root.root.to_string_lossy().into_owned())
|
||||
.collect(),
|
||||
file_system_policy
|
||||
.get_unreadable_roots_with_cwd(cwd)
|
||||
.iter()
|
||||
.map(|path| path.to_string_lossy().into_owned())
|
||||
.collect(),
|
||||
file_system_policy.get_unreadable_globs_with_cwd(cwd),
|
||||
)
|
||||
};
|
||||
let sandbox_mode = if full_disk_write {
|
||||
SandboxMode::DangerFullAccess
|
||||
} else if writable_roots.is_empty() {
|
||||
SandboxMode::ReadOnly
|
||||
} else {
|
||||
SandboxMode::WorkspaceWrite
|
||||
};
|
||||
writable_roots.sort_by(|left, right| left.root.as_path().cmp(right.root.as_path()));
|
||||
let writable_roots = writable_roots
|
||||
.iter()
|
||||
.map(|root| root.root.to_string_lossy().into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
let denied_read_paths = file_system_policy
|
||||
.get_unreadable_roots_with_cwd(cwd)
|
||||
.iter()
|
||||
.map(|path| path.to_string_lossy().into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
let denied_read_globs = file_system_policy.get_unreadable_globs_with_cwd(cwd);
|
||||
Self::from_resolved(
|
||||
PermissionsRenderContext {
|
||||
sandbox_mode,
|
||||
|
||||
@@ -10,6 +10,7 @@ use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::permissions::FileSystemSpecialPath;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_absolute_path::test_support::test_path_buf;
|
||||
@@ -33,17 +34,38 @@ fn user_approval_context() -> ApprovalPromptContext<'static> {
|
||||
|
||||
#[test]
|
||||
fn builds_permissions_from_profile() {
|
||||
let cwd = test_path_buf("/tmp");
|
||||
let cwd = test_path_buf("/workspace");
|
||||
let writable_root =
|
||||
AbsolutePathBuf::from_absolute_path(cwd.join("repo")).expect("absolute path");
|
||||
let denied_root =
|
||||
AbsolutePathBuf::from_absolute_path(cwd.join("blocked")).expect("absolute path");
|
||||
let denied_glob = cwd
|
||||
.join("blocked")
|
||||
.join("**")
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions(
|
||||
&FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path {
|
||||
path: writable_root.clone().into(),
|
||||
&FileSystemSandboxPolicy::restricted(vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path {
|
||||
path: writable_root.clone().into(),
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
missing_path_behavior: None,
|
||||
}]),
|
||||
FileSystemSandboxEntry::new(
|
||||
FileSystemPath::Path {
|
||||
path: denied_root.clone().into(),
|
||||
},
|
||||
FileSystemAccessMode::Deny,
|
||||
),
|
||||
FileSystemSandboxEntry::new(
|
||||
FileSystemPath::GlobPattern {
|
||||
pattern: denied_glob.clone(),
|
||||
},
|
||||
FileSystemAccessMode::Deny,
|
||||
),
|
||||
]),
|
||||
NetworkSandboxPolicy::Enabled,
|
||||
);
|
||||
|
||||
@@ -53,6 +75,7 @@ fn builds_permissions_from_profile() {
|
||||
user_approval_context(),
|
||||
&Policy::empty(),
|
||||
&cwd,
|
||||
/*paths*/ None,
|
||||
/*exec_permission_approvals_enabled*/ false,
|
||||
/*request_permissions_tool_enabled*/ false,
|
||||
);
|
||||
@@ -60,33 +83,50 @@ fn builds_permissions_from_profile() {
|
||||
assert!(text.contains("`sandbox_mode` is `workspace-write`"));
|
||||
assert!(text.contains("Network access is enabled."));
|
||||
assert!(text.contains(writable_root.to_string_lossy().as_ref()));
|
||||
assert!(text.contains("Do not request escalation or additional permissions"));
|
||||
assert!(text.contains(&format!("path `{}`", denied_root.display())));
|
||||
assert!(text.contains(&format!("glob `{denied_glob}`")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_permissions_from_profile_with_denied_reads() {
|
||||
let cwd = test_path_buf("/tmp");
|
||||
let denied_root =
|
||||
AbsolutePathBuf::from_absolute_path(cwd.join("blocked")).expect("absolute path");
|
||||
let denied_glob = cwd.join("blocked").join("**");
|
||||
fn builds_permissions_from_profile_with_executor_denied_reads() {
|
||||
let paths = FileSystemSandboxPolicyContext {
|
||||
cwd: &"file:///C:/workspace".parse().unwrap(),
|
||||
workspace_roots: &[],
|
||||
user_home_dir: None,
|
||||
temporary_directories: None,
|
||||
};
|
||||
let denied_root = paths.cwd.join("blocked").unwrap();
|
||||
let denied_glob = r"C:\workspace\blocked\**";
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions(
|
||||
&FileSystemSandboxPolicy::restricted(vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
|
||||
value: FileSystemSpecialPath::Root,
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path {
|
||||
path: denied_root.clone().into(),
|
||||
FileSystemSandboxEntry::new(
|
||||
FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::Tmpdir,
|
||||
},
|
||||
FileSystemAccessMode::Deny,
|
||||
),
|
||||
FileSystemSandboxEntry::new(
|
||||
FileSystemPath::GlobPattern {
|
||||
pattern: "~/private/**".into(),
|
||||
},
|
||||
FileSystemAccessMode::Deny,
|
||||
),
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path { path: denied_root },
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::GlobPattern {
|
||||
pattern: denied_glob.to_string_lossy().into_owned(),
|
||||
pattern: denied_glob.into(),
|
||||
},
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
@@ -103,15 +143,16 @@ fn builds_permissions_from_profile_with_denied_reads() {
|
||||
ResolvedModelMessages::bundled(),
|
||||
),
|
||||
&Policy::empty(),
|
||||
&cwd,
|
||||
&test_path_buf("/unused-local-cwd"),
|
||||
Some(&paths),
|
||||
/*exec_permission_approvals_enabled*/ false,
|
||||
/*request_permissions_tool_enabled*/ false,
|
||||
);
|
||||
let text = instructions.body();
|
||||
assert!(text.contains("## Denied filesystem reads"));
|
||||
assert!(text.contains("Do not request escalation or additional permissions"));
|
||||
assert!(text.contains(denied_root.to_string_lossy().as_ref()));
|
||||
assert!(text.contains(&format!("glob `{}`", denied_glob.to_string_lossy())));
|
||||
assert!(text.contains(r"path `C:\workspace\blocked`"));
|
||||
assert!(text.contains(&format!("glob `{denied_glob}`")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -704,3 +745,32 @@ fn preserves_supplied_path_spellings_and_order() {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bounds_permission_paths_at_whole_entry_boundaries() {
|
||||
let render =
|
||||
|writable_roots: &[String], denied_read_paths: &[String], denied_read_globs: &[String]| {
|
||||
PermissionsInstructions::from_resolved(
|
||||
PermissionsRenderContext {
|
||||
writable_roots,
|
||||
denied_read_paths,
|
||||
denied_read_globs,
|
||||
..WORKSPACE_CONTEXT
|
||||
},
|
||||
user_approval_context(),
|
||||
)
|
||||
.body()
|
||||
};
|
||||
// Any two fit, but all three exceed the shared budget. Include multibyte paths.
|
||||
let writable = format!("/writable/{}", "é".repeat(6_000));
|
||||
let denied = format!("/denied/{}", "é".repeat(6_000));
|
||||
let glob = format!("/glob/{}/**", "é".repeat(6_000));
|
||||
let oversized = format!("/oversized/{}", "x".repeat(MAX_PERMISSION_PATH_BYTES));
|
||||
let text = render(
|
||||
&[oversized, writable.clone()],
|
||||
std::slice::from_ref(&denied),
|
||||
&[glob, "/short/**".into()],
|
||||
);
|
||||
let expected = render(&[writable], &[denied], &["/short/**".into()]);
|
||||
assert_eq!(text, format!("{expected}{OMITTED_PERMISSION_PATHS}\n"));
|
||||
}
|
||||
|
||||
@@ -1827,7 +1827,9 @@ impl FileSystemSandboxPolicy {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn resolved_entries(
|
||||
/// Resolves configured roots using executor paths without inspecting the filesystem.
|
||||
/// Glob patterns are excluded; access precedence is evaluated by `resolve_access`.
|
||||
pub fn resolved_entries(
|
||||
&self,
|
||||
context: &FileSystemSandboxPolicyContext<'_>,
|
||||
) -> Vec<(PathUri, FileSystemAccessMode)> {
|
||||
|
||||
Reference in New Issue
Block a user