Codex Handbook
core/src/config/permissions.rs 916 lines
use std::borrow::Cow;use std::collections::BTreeMap;use std::io;use std::path::Component;use std::path::Path;use std::path::PathBuf;use codex_config::permissions_toml::FilesystemPermissionToml;use codex_config::permissions_toml::FilesystemPermissionsToml;use codex_config::permissions_toml::NetworkDomainPermissionToml;use codex_config::permissions_toml::NetworkDomainPermissionsToml;use codex_config::permissions_toml::NetworkToml;use codex_config::permissions_toml::NetworkUnixSocketPermissionToml;use codex_config::permissions_toml::NetworkUnixSocketPermissionsToml;use codex_config::permissions_toml::PermissionProfileToml;use codex_config::permissions_toml::PermissionsToml;use codex_config::permissions_toml::WorkspaceRootsToml;use codex_config::types::SandboxWorkspaceWrite;use codex_features::NetworkProxyConfigToml;use codex_features::NetworkProxyDomainPermissionToml;use codex_features::NetworkProxyModeToml;use codex_features::NetworkProxyUnixSocketPermissionToml;use codex_network_proxy::NetworkMode;use codex_network_proxy::NetworkProxyConfig;#[cfg(test)]use codex_network_proxy::NetworkUnixSocketPermission as ProxyNetworkUnixSocketPermission;use codex_protocol::config_types::WindowsSandboxLevel;use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS;use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_READ_ONLY;use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_WORKSPACE;use codex_protocol::models::PermissionProfile;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_protocol::permissions::project_roots_glob_pattern;use codex_utils_absolute_path::AbsolutePathBuf;use super::ProjectConfig;pub(crate) const BUILT_IN_READ_ONLY_PROFILE: &str = BUILT_IN_PERMISSION_PROFILE_READ_ONLY;pub(crate) const BUILT_IN_WORKSPACE_PROFILE: &str = BUILT_IN_PERMISSION_PROFILE_WORKSPACE;pub(crate) const BUILT_IN_DANGER_FULL_ACCESS_PROFILE: &str =    BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS;pub(crate) fn default_builtin_permission_profile_name(    active_project: &ProjectConfig,    windows_sandbox_level: WindowsSandboxLevel,) -> &'static str {    if (active_project.is_trusted() || active_project.is_untrusted())        && !(cfg!(target_os = "windows") && windows_sandbox_level == WindowsSandboxLevel::Disabled)    {        BUILT_IN_WORKSPACE_PROFILE    } else {        BUILT_IN_READ_ONLY_PROFILE    }}pub(crate) fn is_builtin_permission_profile_name(profile_name: &str) -> bool {    matches!(        profile_name,        BUILT_IN_READ_ONLY_PROFILE            | BUILT_IN_WORKSPACE_PROFILE            | BUILT_IN_DANGER_FULL_ACCESS_PROFILE    )}pub(crate) fn builtin_permission_profile(    profile_name: &str,    workspace_write: Option<&SandboxWorkspaceWrite>,) -> Option<PermissionProfile> {    match profile_name {        BUILT_IN_READ_ONLY_PROFILE => Some(PermissionProfile::read_only()),        BUILT_IN_WORKSPACE_PROFILE => Some(match workspace_write {            Some(SandboxWorkspaceWrite {                writable_roots: _,                network_access,                exclude_tmpdir_env_var,                exclude_slash_tmp,            }) => PermissionProfile::workspace_write_with(                &[],                if *network_access {                    NetworkSandboxPolicy::Enabled                } else {                    NetworkSandboxPolicy::Restricted                },                *exclude_tmpdir_env_var,                *exclude_slash_tmp,            ),            None => PermissionProfile::workspace_write(),        }),        BUILT_IN_DANGER_FULL_ACCESS_PROFILE => Some(PermissionProfile::Disabled),        _ => None,    }}pub(crate) fn validate_user_permission_profile_names(    permissions: Option<&PermissionsToml>,) -> io::Result<()> {    let Some(permissions) = permissions else {        return Ok(());    };    for profile_name in permissions.entries.keys() {        if profile_name.starts_with(':') {            return Err(io::Error::new(                io::ErrorKind::InvalidInput,                format!(                    "permissions profile `{profile_name}` uses a reserved built-in profile prefix"                ),            ));        }    }    Ok(())}pub(crate) fn network_proxy_config_from_profile_network(    network: Option<&NetworkToml>,) -> NetworkProxyConfig {    let mut config = network.map_or_else(        NetworkProxyConfig::default,        NetworkToml::to_network_proxy_config,    );    // Profile `network.enabled` controls sandbox network access. Profiles may    // provide proxy settings for the feature gate to consume when that network    // access is enabled, but they do not start the managed proxy on their own.    config.network.enabled = false;    config}pub(crate) fn apply_network_proxy_feature_config(    config: &mut NetworkProxyConfig,    feature_config: &NetworkProxyConfigToml,) {    NetworkToml {        enabled: feature_config.enabled,        proxy_url: feature_config.proxy_url.clone(),        enable_socks5: feature_config.enable_socks5,        socks_url: feature_config.socks_url.clone(),        enable_socks5_udp: feature_config.enable_socks5_udp,        allow_upstream_proxy: feature_config.allow_upstream_proxy,        dangerously_allow_non_loopback_proxy: feature_config.dangerously_allow_non_loopback_proxy,        dangerously_allow_all_unix_sockets: feature_config.dangerously_allow_all_unix_sockets,        mode: feature_config.mode.map(|mode| match mode {            NetworkProxyModeToml::Limited => NetworkMode::Limited,            NetworkProxyModeToml::Full => NetworkMode::Full,        }),        domains: feature_config            .domains            .as_ref()            .map(|domains| NetworkDomainPermissionsToml {                entries: domains                    .iter()                    .map(|(pattern, permission)| {                        let permission = match permission {                            NetworkProxyDomainPermissionToml::Allow => {                                NetworkDomainPermissionToml::Allow                            }                            NetworkProxyDomainPermissionToml::Deny => {                                NetworkDomainPermissionToml::Deny                            }                        };                        (pattern.clone(), permission)                    })                    .collect(),            }),        unix_sockets: feature_config.unix_sockets.as_ref().map(|unix_sockets| {            NetworkUnixSocketPermissionsToml {                entries: unix_sockets                    .iter()                    .map(|(path, permission)| {                        let permission = match permission {                            NetworkProxyUnixSocketPermissionToml::Allow => {                                NetworkUnixSocketPermissionToml::Allow                            }                            NetworkProxyUnixSocketPermissionToml::Deny => {                                NetworkUnixSocketPermissionToml::Deny                            }                        };                        (path.clone(), permission)                    })                    .collect(),            }        }),        allow_local_binding: feature_config.allow_local_binding,        mitm: None,    }    .apply_to_network_proxy_config(config);}pub(crate) fn resolve_permission_profile(    permissions: &PermissionsToml,    profile_name: &str,) -> io::Result<PermissionProfileToml> {    permissions        .resolve_profile(profile_name, extensible_builtin_parent_profile)        .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err.to_string()))}fn extensible_builtin_parent_profile(profile_name: &str) -> Option<PermissionProfileToml> {    let file_system = match profile_name {        BUILT_IN_READ_ONLY_PROFILE => FileSystemSandboxPolicy::read_only(),        BUILT_IN_WORKSPACE_PROFILE => FileSystemSandboxPolicy::workspace_write(            &[],            /*exclude_tmpdir_env_var*/ false,            /*exclude_slash_tmp*/ false,        ),        _ => return None,    };    Some(permission_profile_toml_from_file_system_policy(file_system))}fn permission_profile_toml_from_file_system_policy(    file_system: FileSystemSandboxPolicy,) -> PermissionProfileToml {    let mut filesystem = FilesystemPermissionsToml {        glob_scan_max_depth: file_system.glob_scan_max_depth,        entries: BTreeMap::new(),    };    for entry in file_system.entries {        insert_filesystem_permission_toml(&mut filesystem.entries, entry);    }    PermissionProfileToml {        description: None,        extends: None,        workspace_roots: None,        filesystem: Some(filesystem),        network: None,    }}fn insert_filesystem_permission_toml(    entries: &mut BTreeMap<String, FilesystemPermissionToml>,    entry: FileSystemSandboxEntry,) {    match entry.path {        FileSystemPath::Path { path } => {            entries.insert(                path.into_path_buf().to_string_lossy().into_owned(),                FilesystemPermissionToml::Access(entry.access),            );        }        FileSystemPath::GlobPattern { pattern } => {            entries.insert(pattern, FilesystemPermissionToml::Access(entry.access));        }        FileSystemPath::Special { value } => {            insert_special_filesystem_permission_toml(entries, value, entry.access);        }    }}fn insert_special_filesystem_permission_toml(    entries: &mut BTreeMap<String, FilesystemPermissionToml>,    value: FileSystemSpecialPath,    access: FileSystemAccessMode,) {    match value {        FileSystemSpecialPath::Root => {            entries.insert(                ":root".to_string(),                FilesystemPermissionToml::Access(access),            );        }        FileSystemSpecialPath::Minimal => {            entries.insert(                ":minimal".to_string(),                FilesystemPermissionToml::Access(access),            );        }        FileSystemSpecialPath::ProjectRoots { subpath } => {            insert_scoped_filesystem_permission_toml(                entries,                ":workspace_roots".to_string(),                subpath.unwrap_or_else(|| PathBuf::from(".")),                access,            );        }        FileSystemSpecialPath::Tmpdir => {            entries.insert(                ":tmpdir".to_string(),                FilesystemPermissionToml::Access(access),            );        }        FileSystemSpecialPath::SlashTmp => {            entries.insert(                ":slash_tmp".to_string(),                FilesystemPermissionToml::Access(access),            );        }        FileSystemSpecialPath::Unknown { path, subpath } => {            if let Some(subpath) = subpath {                insert_scoped_filesystem_permission_toml(entries, path, subpath, access);            } else {                entries.insert(path, FilesystemPermissionToml::Access(access));            }        }    };}fn insert_scoped_filesystem_permission_toml(    entries: &mut BTreeMap<String, FilesystemPermissionToml>,    path: String,    subpath: PathBuf,    access: FileSystemAccessMode,) {    let permission = entries        .entry(path)        .or_insert_with(|| FilesystemPermissionToml::Scoped(BTreeMap::new()));    match permission {        FilesystemPermissionToml::Scoped(scoped_entries) => {            scoped_entries.insert(subpath.to_string_lossy().into_owned(), access);        }        FilesystemPermissionToml::Access(_) => {            *permission = FilesystemPermissionToml::Scoped(BTreeMap::from([(                subpath.to_string_lossy().into_owned(),                access,            )]));        }    }}pub(crate) fn network_proxy_config_for_profile_selection(    permissions: Option<&PermissionsToml>,    profile_name: &str,) -> io::Result<NetworkProxyConfig> {    if is_builtin_permission_profile_name(profile_name) {        return Ok(NetworkProxyConfig::default());    }    reject_unknown_builtin_permission_profile(profile_name)?;    let permissions = permissions.ok_or_else(|| {        io::Error::new(            io::ErrorKind::InvalidInput,            "default_permissions requires a `[permissions]` table",        )    })?;    let profile = resolve_permission_profile(permissions, profile_name)?;    Ok(network_proxy_config_from_profile_network(        profile.network.as_ref(),    ))}pub(crate) fn compile_permission_profile(    permissions: &PermissionsToml,    profile_name: &str,    policy_cwd: &Path,    startup_warnings: &mut Vec<String>,) -> io::Result<(FileSystemSandboxPolicy, NetworkSandboxPolicy)> {    let profile = resolve_permission_profile(permissions, profile_name)?;    let mut file_system_sandbox_policy = FileSystemSandboxPolicy::restricted(Vec::new());    let base_network_sandbox_policy = NetworkSandboxPolicy::Restricted;    if let Some(filesystem) = profile.filesystem.as_ref() {        if filesystem.is_empty() && file_system_sandbox_policy.entries.is_empty() {            push_warning(                startup_warnings,                missing_filesystem_entries_warning(profile_name),            );        } else {            if cfg!(not(target_os = "macos")) {                for pattern in unsupported_read_write_glob_paths(filesystem) {                    push_warning(                        startup_warnings,                        format!(                            "Filesystem glob `{pattern}` uses `read` or `write` access, which is not fully supported by this platform's sandboxing. Use an exact path or trailing `/**` subtree rule instead. `deny` globs are supported."                        ),                    );                }                for pattern in unbounded_unreadable_globstar_paths(filesystem) {                    push_warning(                        startup_warnings,                        format!(                            "Filesystem deny-read glob `{pattern}` uses `**`. Non-macOS sandboxing does not support unbounded `**` natively; set `glob_scan_max_depth` in this filesystem profile to cap Linux glob expansion and silence this warning, or enumerate explicit depths such as `*.env`, `*/*.env`, and `*/*/*.env`."                        ),                    );                }            }            for (path, permission) in &filesystem.entries {                file_system_sandbox_policy                    .entries                    .extend(compile_filesystem_permission(                        path,                        permission,                        policy_cwd,                        startup_warnings,                    )?);            }        }    } else if file_system_sandbox_policy.entries.is_empty() {        push_warning(            startup_warnings,            missing_filesystem_entries_warning(profile_name),        );    }    let glob_scan_max_depth = validate_glob_scan_max_depth(        profile            .filesystem            .as_ref()            .and_then(|filesystem| filesystem.glob_scan_max_depth),    )?;    if let Some(glob_scan_max_depth) = glob_scan_max_depth {        file_system_sandbox_policy.glob_scan_max_depth = Some(glob_scan_max_depth);    }    let network_sandbox_policy =        compile_network_sandbox_policy(profile.network.as_ref(), base_network_sandbox_policy);    Ok((file_system_sandbox_policy, network_sandbox_policy))}pub(crate) fn compile_permission_profile_selection(    permissions: Option<&PermissionsToml>,    profile_name: &str,    workspace_write: Option<&SandboxWorkspaceWrite>,    policy_cwd: &Path,    startup_warnings: &mut Vec<String>,) -> io::Result<(FileSystemSandboxPolicy, NetworkSandboxPolicy)> {    if let Some(permission_profile) = builtin_permission_profile(profile_name, workspace_write) {        return Ok(permission_profile.to_runtime_permissions());    }    reject_unknown_builtin_permission_profile(profile_name)?;    let permissions = permissions.ok_or_else(|| {        io::Error::new(            io::ErrorKind::InvalidInput,            "default_permissions requires a `[permissions]` table",        )    })?;    compile_permission_profile(permissions, profile_name, policy_cwd, startup_warnings)}pub(crate) fn compile_permission_profile_workspace_roots(    permissions: Option<&PermissionsToml>,    profile_name: &str,    policy_cwd: &Path,) -> io::Result<Vec<AbsolutePathBuf>> {    if is_builtin_permission_profile_name(profile_name) {        return Ok(Vec::new());    }    reject_unknown_builtin_permission_profile(profile_name)?;    let permissions = permissions.ok_or_else(|| {        io::Error::new(            io::ErrorKind::InvalidInput,            "default_permissions requires a `[permissions]` table",        )    })?;    let profile = resolve_permission_profile(permissions, profile_name)?;    Ok(compile_workspace_roots(        profile.workspace_roots.as_ref(),        policy_cwd,    ))}fn compile_workspace_roots(    workspace_roots: Option<&WorkspaceRootsToml>,    policy_cwd: &Path,) -> Vec<AbsolutePathBuf> {    workspace_roots.map_or_else(Vec::new, |workspace_roots| {        workspace_roots            .enabled_roots()            .map(|path| AbsolutePathBuf::resolve_path_against_base(path, policy_cwd))            .collect()    })}pub(crate) fn reject_unknown_builtin_permission_profile(profile_name: &str) -> io::Result<()> {    if profile_name.starts_with(':') {        return Err(io::Error::new(            io::ErrorKind::InvalidInput,            format!("default_permissions refers to unknown built-in profile `{profile_name}`"),        ));    }    Ok(())}/// Returns a list of paths that must be readable by shell tools in order/// for Codex to function. These should always be added to the/// `FileSystemSandboxPolicy` for a thread.pub(crate) fn get_readable_roots_required_for_codex_runtime(    codex_home: &Path,    zsh_path: Option<&PathBuf>,    main_execve_wrapper_exe: Option<&PathBuf>,) -> Vec<AbsolutePathBuf> {    let arg0_root = AbsolutePathBuf::from_absolute_path(codex_home.join("tmp").join("arg0")).ok();    let zsh_path = zsh_path.and_then(|path| AbsolutePathBuf::from_absolute_path(path).ok());    let execve_wrapper_root = main_execve_wrapper_exe.and_then(|path| {        let path = AbsolutePathBuf::from_absolute_path(path).ok()?;        if let Some(arg0_root) = arg0_root.as_ref()            && path.as_path().starts_with(arg0_root.as_path())        {            path.parent()        } else {            Some(path)        }    });    let mut readable_roots = Vec::new();    if let Some(zsh_path) = zsh_path {        readable_roots.push(zsh_path);    }    if let Some(execve_wrapper_root) = execve_wrapper_root {        readable_roots.push(execve_wrapper_root);    }    readable_roots}fn compile_network_sandbox_policy(    network: Option<&NetworkToml>,    base_network_sandbox_policy: NetworkSandboxPolicy,) -> NetworkSandboxPolicy {    let Some(network) = network else {        return base_network_sandbox_policy;    };    match network.enabled {        Some(true) => NetworkSandboxPolicy::Enabled,        Some(false) => NetworkSandboxPolicy::Restricted,        None => base_network_sandbox_policy,    }}fn compile_filesystem_permission(    path: &str,    permission: &FilesystemPermissionToml,    policy_cwd: &Path,    startup_warnings: &mut Vec<String>,) -> io::Result<Vec<FileSystemSandboxEntry>> {    let mut entries = Vec::new();    match permission {        FilesystemPermissionToml::Access(access) => {            entries.push(FileSystemSandboxEntry {                path: compile_filesystem_access_path(path, *access, startup_warnings)?,                access: *access,            });        }        FilesystemPermissionToml::Scoped(scoped_entries) => {            for (subpath, access) in scoped_entries {                let has_glob = contains_glob_chars(subpath);                let can_compile_as_pattern = match parse_special_path(path) {                    Some(FileSystemSpecialPath::ProjectRoots { .. }) | None => true,                    Some(_) => false,                };                if has_glob && *access == FileSystemAccessMode::Deny && can_compile_as_pattern {                    // Scoped glob syntax is a first-class filesystem policy                    // pattern entry. Literal scoped paths continue through the                    // exact-path parser so existing path semantics stay intact.                    let entry = FileSystemSandboxEntry {                        path: FileSystemPath::GlobPattern {                            pattern: compile_scoped_filesystem_pattern(                                path, subpath, *access, policy_cwd,                            )?,                        },                        access: *access,                    };                    entries.push(entry);                } else {                    let subpath = compile_read_write_glob_path(subpath, *access)?;                    entries.push(FileSystemSandboxEntry {                        path: compile_scoped_filesystem_path(path, subpath, startup_warnings)?,                        access: *access,                    });                }            }        }    }    Ok(entries)}fn compile_filesystem_access_path(    path: &str,    access: FileSystemAccessMode,    startup_warnings: &mut Vec<String>,) -> io::Result<FileSystemPath> {    if !contains_glob_chars(path) {        return compile_filesystem_path(path, startup_warnings);    }    if access == FileSystemAccessMode::Deny {        // At this point `path` is an unscoped filesystem table key. Top-level        // glob deny entries still go through the absolute-path parser before        // becoming policy patterns; relative project-root glob syntax is        // handled by `compile_scoped_filesystem_pattern`.        return Ok(FileSystemPath::GlobPattern {            pattern: parse_absolute_path(path)?.to_string_lossy().into_owned(),        });    }    let path = compile_read_write_glob_path(path, access)?;    compile_filesystem_path(path, startup_warnings)}fn compile_filesystem_path(    path: &str,    startup_warnings: &mut Vec<String>,) -> io::Result<FileSystemPath> {    if let Some(special) = parse_special_path(path) {        maybe_push_unknown_special_path_warning(&special, startup_warnings);        return Ok(FileSystemPath::Special { value: special });    }    let path = parse_absolute_path(path)?;    Ok(FileSystemPath::Path { path })}fn compile_scoped_filesystem_path(    path: &str,    subpath: &str,    startup_warnings: &mut Vec<String>,) -> io::Result<FileSystemPath> {    if subpath == "." {        return compile_filesystem_path(path, startup_warnings);    }    if let Some(special) = parse_special_path(path) {        let subpath = parse_relative_subpath(subpath)?;        let special = match special {            FileSystemSpecialPath::ProjectRoots { .. } => Ok(FileSystemPath::Special {                value: FileSystemSpecialPath::project_roots(Some(subpath)),            }),            FileSystemSpecialPath::Unknown { path, .. } => Ok(FileSystemPath::Special {                value: FileSystemSpecialPath::unknown(path, Some(subpath)),            }),            _ => Err(io::Error::new(                io::ErrorKind::InvalidInput,                format!("filesystem path `{path}` does not support nested entries"),            )),        }?;        if let FileSystemPath::Special { value } = &special {            maybe_push_unknown_special_path_warning(value, startup_warnings);        }        return Ok(special);    }    let subpath = parse_relative_subpath(subpath)?;    let base = parse_absolute_path(path)?;    let path = AbsolutePathBuf::resolve_path_against_base(&subpath, base.as_path());    Ok(FileSystemPath::Path { path })}fn compile_scoped_filesystem_pattern(    path: &str,    subpath: &str,    access: FileSystemAccessMode,    _policy_cwd: &Path,) -> io::Result<String> {    // Pattern entries currently mean deny-read only. Supporting broader access    // modes here would imply glob-based read/write allow semantics that the    // sandbox policy does not express yet.    if access != FileSystemAccessMode::Deny {        return Err(io::Error::new(            io::ErrorKind::InvalidInput,            format!("filesystem glob subpath `{subpath}` only supports `deny` access"),        ));    }    let subpath = parse_relative_subpath(subpath)?;    match parse_special_path(path) {        Some(FileSystemSpecialPath::ProjectRoots { .. }) => {            // Keep `:workspace_roots` glob patterns symbolic until the active            // workspace roots are known, then materialize them for cwd and any            // runtime/profile-added workspace roots together.            Ok(project_roots_glob_pattern(&subpath))        }        Some(_) => Err(io::Error::new(            io::ErrorKind::InvalidInput,            format!("filesystem path `{path}` does not support nested entries"),        )),        None => {            let base = parse_absolute_path(path)?;            Ok(base.join(&subpath).to_string_lossy().to_string())        }    }}fn compile_read_write_glob_path(path: &str, access: FileSystemAccessMode) -> io::Result<&str> {    if !contains_glob_chars(path) {        return Ok(path);    }    let path_without_trailing_glob = remove_trailing_glob_suffix(path);    if !contains_glob_chars(path_without_trailing_glob) {        return Ok(path_without_trailing_glob);    }    Err(io::Error::new(        io::ErrorKind::InvalidInput,        format!(            "filesystem glob path `{path}` only supports `deny` access; use an exact path or trailing `/**` for `{access}` subtree access"        ),    ))}fn unsupported_read_write_glob_paths(filesystem: &FilesystemPermissionsToml) -> Vec<String> {    let mut patterns = Vec::new();    for (path, permission) in &filesystem.entries {        match permission {            FilesystemPermissionToml::Access(access) => {                if *access != FileSystemAccessMode::Deny                    && contains_glob_chars(remove_trailing_glob_suffix(path))                {                    patterns.push(path.clone());                }            }            FilesystemPermissionToml::Scoped(scoped_entries) => {                for (subpath, access) in scoped_entries {                    if *access != FileSystemAccessMode::Deny                        && contains_glob_chars(remove_trailing_glob_suffix(subpath))                    {                        patterns.push(format!("{path}/{subpath}"));                    }                }            }        }    }    patterns}fn unbounded_unreadable_globstar_paths(filesystem: &FilesystemPermissionsToml) -> Vec<String> {    if filesystem.glob_scan_max_depth.is_some() {        return Vec::new();    }    let mut patterns = Vec::new();    for (path, permission) in &filesystem.entries {        match permission {            FilesystemPermissionToml::Access(FileSystemAccessMode::Deny) => {                if path.contains("**") {                    patterns.push(path.clone());                }            }            FilesystemPermissionToml::Access(_) => {}            FilesystemPermissionToml::Scoped(scoped_entries) => {                for (subpath, access) in scoped_entries {                    if *access == FileSystemAccessMode::Deny && subpath.contains("**") {                        patterns.push(format!("{path}/{subpath}"));                    }                }            }        }    }    patterns}fn validate_glob_scan_max_depth(max_depth: Option<usize>) -> io::Result<Option<usize>> {    match max_depth {        Some(0) => Err(io::Error::new(            io::ErrorKind::InvalidInput,            "glob_scan_max_depth must be at least 1",        )),        _ => Ok(max_depth),    }}fn contains_glob_chars(path: &str) -> bool {    contains_glob_chars_for_platform(path, cfg!(windows))}fn contains_glob_chars_for_platform(path: &str, is_windows: bool) -> bool {    let normalized_windows_path = if is_windows {        normalize_windows_device_path(path)    } else {        None    };    let path = normalized_windows_path.as_deref().unwrap_or(path);    path.chars().any(|ch| matches!(ch, '*' | '?' | '[' | ']'))}fn remove_trailing_glob_suffix(path: &str) -> &str {    path.strip_suffix("/**").unwrap_or(path)}// WARNING: keep this parser forward-compatible.// Adding a new `:special_path` must not make older Codex versions reject the// config. Unknown values intentionally round-trip through// `FileSystemSpecialPath::Unknown` so they can be surfaced as warnings and// ignored, rather than aborting config load.fn parse_special_path(path: &str) -> Option<FileSystemSpecialPath> {    match path {        ":root" => Some(FileSystemSpecialPath::Root),        ":minimal" => Some(FileSystemSpecialPath::Minimal),        ":workspace_roots" => Some(FileSystemSpecialPath::project_roots(/*subpath*/ None)),        ":tmpdir" => Some(FileSystemSpecialPath::Tmpdir),        ":slash_tmp" => Some(FileSystemSpecialPath::SlashTmp),        _ if path.starts_with(':') => {            Some(FileSystemSpecialPath::unknown(path, /*subpath*/ None))        }        _ => None,    }}fn parse_absolute_path(path: &str) -> io::Result<AbsolutePathBuf> {    parse_absolute_path_for_platform(path, cfg!(windows))}fn parse_absolute_path_for_platform(path: &str, is_windows: bool) -> io::Result<AbsolutePathBuf> {    let path_ref = normalize_absolute_path_for_platform(path, is_windows);    if !is_absolute_path_for_platform(path, path_ref.as_ref(), is_windows)        && path != "~"        && !path.starts_with("~/")    {        return Err(io::Error::new(            io::ErrorKind::InvalidInput,            format!("filesystem path `{path}` must be absolute, use `~/...`, or start with `:`"),        ));    }    AbsolutePathBuf::from_absolute_path(path_ref.as_ref())}fn is_absolute_path_for_platform(path: &str, normalized_path: &Path, is_windows: bool) -> bool {    if is_windows {        is_windows_absolute_path(path)            || is_windows_absolute_path(&normalized_path.to_string_lossy())    } else {        normalized_path.is_absolute()    }}fn normalize_absolute_path_for_platform(path: &str, is_windows: bool) -> Cow<'_, Path> {    if !is_windows {        return Cow::Borrowed(Path::new(path));    }    match normalize_windows_device_path(path) {        Some(normalized) => Cow::Owned(PathBuf::from(normalized)),        None => Cow::Borrowed(Path::new(path)),    }}fn normalize_windows_device_path(path: &str) -> Option<String> {    if let Some(unc) = path.strip_prefix(r"\\?\UNC\") {        return Some(format!(r"\\{unc}"));    }    if let Some(unc) = path.strip_prefix(r"\\.\UNC\") {        return Some(format!(r"\\{unc}"));    }    if let Some(path) = path.strip_prefix(r"\\?\")        && is_windows_drive_absolute_path(path)    {        return Some(path.to_string());    }    if let Some(path) = path.strip_prefix(r"\\.\")        && is_windows_drive_absolute_path(path)    {        return Some(path.to_string());    }    None}fn is_windows_absolute_path(path: &str) -> bool {    is_windows_drive_absolute_path(path) || path.starts_with(r"\\")}fn is_windows_drive_absolute_path(path: &str) -> bool {    let bytes = path.as_bytes();    bytes.len() >= 3        && bytes[0].is_ascii_alphabetic()        && bytes[1] == b':'        && matches!(bytes[2], b'\\' | b'/')}fn parse_relative_subpath(subpath: &str) -> io::Result<PathBuf> {    let path = Path::new(subpath);    if !subpath.is_empty()        && path            .components()            .all(|component| matches!(component, Component::Normal(_)))    {        return Ok(path.to_path_buf());    }    Err(io::Error::new(        io::ErrorKind::InvalidInput,        format!(            "filesystem subpath `{}` must be a descendant path without `.` or `..` components",            path.display()        ),    ))}fn push_warning(startup_warnings: &mut Vec<String>, message: String) {    tracing::warn!("{message}");    startup_warnings.push(message);}fn missing_filesystem_entries_warning(profile_name: &str) -> String {    format!(        "Permissions profile `{profile_name}` does not define any recognized filesystem entries for this version of Codex. Filesystem access will remain restricted. Upgrade Codex if this profile expects filesystem permissions."    )}fn maybe_push_unknown_special_path_warning(    special: &FileSystemSpecialPath,    startup_warnings: &mut Vec<String>,) {    let FileSystemSpecialPath::Unknown { path, subpath } = special else {        return;    };    push_warning(        startup_warnings,        match subpath.as_deref() {            Some(subpath) => format!(                "Configured filesystem path `{path}` with nested entry `{}` is not recognized by this version of Codex and will be ignored. Upgrade Codex if this path is required.",                subpath.display()            ),            None => format!(                "Configured filesystem path `{path}` is not recognized by this version of Codex and will be ignored. Upgrade Codex if this path is required."            ),        },    );}#[cfg(test)]#[path = "permissions_tests.rs"]mod tests;