Files
codex/codex-rs/config/src/filesystem_constraints.rs
T
Sean Huang cc05ecfe17 Resolve filesystem denials with explicit path context (#44669)
## Why

Filesystem denial paths need to use the owning environment's path syntax, base directory, and home directory. Host-native resolution cannot supply those facts for another platform, and invalid denials must not be silently skipped when building the sandbox policy.

## What changed

- Add `ConfigPathContext` to requirements layers so `permissions.filesystem.deny_read` can resolve using explicit POSIX or Windows path facts, with native defaults when no context is supplied.
- Share URI-based resolution and validation for literal paths and glob prefixes. Reject ambiguous or lossy paths, including NUL bytes, Windows stream syntax, and unsupported UNC spellings.
- Move denial conversion into `FilesystemConstraints::apply_to_policy`, preserving glob patterns and deduplicating entries. Validate all denials before modifying the policy and propagate failures through configuration loading.

## Testing

Add tests for per-layer base and home resolution, Windows drives and globs, nested context restoration, missing home directories, and policy conversion without partial mutation. Add a configuration-loading regression test for a required denial glob containing a NUL byte.

GitOrigin-RevId: fa0da0d149407958e39897a39fe7b87edd6f1644
2026-09-10 23:08:23 +00:00

63 lines
2.4 KiB
Rust

//! Convert resolved filesystem denials into portable policy entries.
use crate::FilesystemConstraints;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_utils_path_uri::LegacyAppPathString;
use codex_utils_path_uri::LegacyAppPathStringError;
use codex_utils_path_uri::PathConvention;
use codex_utils_path_uri::PathUri;
use std::io;
impl FilesystemConstraints {
/// Adds denials without changing existing entries or other policy settings.
/// `convention` must match the executor facts used to resolve these requirements.
/// Literal paths become URIs; glob spellings and entry order are preserved.
/// Invalid denials return an error before the policy is changed.
pub fn apply_to_policy(
&self,
policy: &mut FileSystemSandboxPolicy,
convention: PathConvention,
) -> io::Result<()> {
let entries = self
.deny_read
.iter()
.map(|deny_read| {
PathUri::validate_config_path_text(deny_read.as_str(), convention)?;
let path = if deny_read.contains_glob() {
FileSystemPath::GlobPattern {
pattern: deny_read.as_str().to_string(),
}
} else {
let mut path = LegacyAppPathString::from_string(deny_read.as_str())
.to_path_uri(convention)?;
path.validate_config_path(convention)?;
// Match Core's existing URI spelling for UNC share roots.
if path.to_url().host_str().is_some() {
path = path.join(".")?;
}
path.into()
};
Ok(FileSystemSandboxEntry::new(
path,
FileSystemAccessMode::Deny,
))
})
.collect::<Result<Vec<_>, LegacyAppPathStringError>>()
.map_err(|error| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!("invalid permissions.filesystem.deny_read path: {error}"),
)
})?;
for entry in entries {
if !policy.entries.contains(&entry) {
policy.entries.push(entry);
}
}
Ok(())
}
}