mirror of
https://github.com/openai/codex.git
synced 2026-09-10 20:26:47 +00:00
Extract Windows deny-read glob scan planning into protocol (#43903)
Move lexical scan-bound calculation into `codex_protocol::permissions` and expose `windows_deny_read_glob_scan` with its `WindowsDenyReadGlobScan` result. Have the Windows sandbox resolver use the shared helper, preserving literal scan roots, glob suffixes, and traversal depth limits without filesystem access in the planner. GitOrigin-RevId: 748a12b45f89c6e045e9123c055453ec39c202b1
This commit is contained in:
@@ -24,6 +24,11 @@ use crate::protocol::NetworkAccess;
|
||||
use crate::protocol::SandboxPolicy;
|
||||
use crate::protocol::WritableRoot;
|
||||
|
||||
mod windows_glob;
|
||||
|
||||
pub use windows_glob::WindowsDenyReadGlobScan;
|
||||
pub use windows_glob::windows_deny_read_glob_scan;
|
||||
|
||||
const PROTECTED_METADATA_GIT_PATH_NAME: &str = ".git";
|
||||
const PROTECTED_METADATA_AGENTS_PATH_NAME: &str = ".agents";
|
||||
const PROTECTED_METADATA_CODEX_PATH_NAME: &str = ".codex";
|
||||
|
||||
43
codex-rs/protocol/src/permissions/windows_glob.rs
Normal file
43
codex-rs/protocol/src/permissions/windows_glob.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
//! Windows deny-glob scan bounds shared by policy validation and native ACL expansion.
|
||||
|
||||
/// Literal scan root and maximum traversal depth for a Windows deny glob.
|
||||
pub struct WindowsDenyReadGlobScan<'a> {
|
||||
pub root: &'a str,
|
||||
pub pattern_suffix: &'a str,
|
||||
pub max_depth: Option<usize>,
|
||||
}
|
||||
|
||||
/// Plans lexical scan bounds without accessing the controller's filesystem.
|
||||
pub fn windows_deny_read_glob_scan(
|
||||
pattern: &str,
|
||||
configured_max_depth: Option<usize>,
|
||||
) -> WindowsDenyReadGlobScan<'_> {
|
||||
let first_glob = pattern.find(['*', '?', '[']).unwrap_or(pattern.len());
|
||||
let literal_prefix = &pattern[..first_glob];
|
||||
let (root, pattern_suffix) = match literal_prefix.rfind(['/', '\\']) {
|
||||
Some(index) => {
|
||||
let drive_root = index > 0 && literal_prefix.as_bytes()[index - 1] == b':';
|
||||
let end = if index == 0 || drive_root {
|
||||
index + 1
|
||||
} else {
|
||||
index
|
||||
};
|
||||
(&literal_prefix[..end], &pattern[index + 1..])
|
||||
}
|
||||
None => (".", pattern),
|
||||
};
|
||||
let components = pattern_suffix
|
||||
.split(['/', '\\'])
|
||||
.filter(|component| !component.is_empty())
|
||||
.collect::<Vec<_>>();
|
||||
let max_depth = if components.contains(&"**") {
|
||||
configured_max_depth
|
||||
} else {
|
||||
Some(configured_max_depth.map_or(components.len(), |depth| depth.min(components.len())))
|
||||
};
|
||||
WindowsDenyReadGlobScan {
|
||||
root,
|
||||
pattern_suffix,
|
||||
max_depth,
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::permissions::ReadDenyMatcher;
|
||||
use codex_protocol::permissions::windows_deny_read_glob_scan;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
@@ -211,39 +212,11 @@ fn glob_scan_plans(
|
||||
}
|
||||
|
||||
fn glob_scan_plan(pattern: &str, configured_max_depth: Option<usize>) -> GlobScanPlan {
|
||||
// Start scanning at the deepest literal directory prefix before the first
|
||||
// glob metacharacter. For example, `C:\repo\**\*.env` only scans `C:\repo`
|
||||
// instead of the current directory or drive root.
|
||||
let first_glob = pattern
|
||||
.char_indices()
|
||||
.find(|(_, ch)| matches!(ch, '*' | '?' | '['))
|
||||
.map(|(index, _)| index)
|
||||
.unwrap_or(pattern.len());
|
||||
let literal_prefix = &pattern[..first_glob];
|
||||
let Some(separator_index) = literal_prefix.rfind(['/', '\\']) else {
|
||||
return GlobScanPlan {
|
||||
root: PathBuf::from("."),
|
||||
max_depth: effective_glob_scan_max_depth(pattern, configured_max_depth),
|
||||
globs: vec![ripgrep_glob(pattern)],
|
||||
};
|
||||
};
|
||||
let pattern_suffix = &pattern[separator_index + 1..];
|
||||
let is_drive_root_separator = separator_index > 0
|
||||
&& literal_prefix
|
||||
.as_bytes()
|
||||
.get(separator_index - 1)
|
||||
.is_some_and(|ch| *ch == b':');
|
||||
if separator_index == 0 || is_drive_root_separator {
|
||||
return GlobScanPlan {
|
||||
root: PathBuf::from(&literal_prefix[..=separator_index]),
|
||||
max_depth: effective_glob_scan_max_depth(pattern_suffix, configured_max_depth),
|
||||
globs: vec![ripgrep_glob(pattern_suffix)],
|
||||
};
|
||||
}
|
||||
let scan = windows_deny_read_glob_scan(pattern, configured_max_depth);
|
||||
GlobScanPlan {
|
||||
root: PathBuf::from(literal_prefix[..separator_index].to_string()),
|
||||
max_depth: effective_glob_scan_max_depth(pattern_suffix, configured_max_depth),
|
||||
globs: vec![ripgrep_glob(pattern_suffix)],
|
||||
root: PathBuf::from(scan.root),
|
||||
max_depth: scan.max_depth,
|
||||
globs: vec![ripgrep_glob(scan.pattern_suffix)],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,22 +258,6 @@ fn ripgrep_glob(pattern: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn effective_glob_scan_max_depth(
|
||||
pattern_suffix: &str,
|
||||
configured_max_depth: Option<usize>,
|
||||
) -> Option<usize> {
|
||||
let components = pattern_suffix
|
||||
.split(['/', '\\'])
|
||||
.filter(|component| !component.is_empty())
|
||||
.collect::<Vec<_>>();
|
||||
if components.contains(&"**") {
|
||||
return configured_max_depth;
|
||||
}
|
||||
Some(configured_max_depth.map_or(components.len(), |max_depth| {
|
||||
max_depth.min(components.len())
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "deny_read_resolver_access_tests.rs"]
|
||||
mod access_tests;
|
||||
|
||||
Reference in New Issue
Block a user