feat(permissions): add glob deny-read policy support (#15979)

## Summary
- adds first-class filesystem policy entries for deny-read glob patterns
- parses config such as :project_roots { "**/*.env" = "none" } into
pattern entries
- enforces deny-read patterns in direct read/list helpers
- fails closed for sandbox execution until platform backends enforce
glob patterns in #18096
- preserves split filesystem policy in turn context only when it cannot
be reconstructed from legacy sandbox policy

## Stack
1. This PR - glob deny-read policy/config/direct-tool support
2. #18096 - macOS and Linux sandbox enforcement
3. #17740 - managed deny-read requirements

## Verification
- just fmt
- cargo check -p codex-core -p codex-sandboxing --tests

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
viyatb-oai
2026-04-16 10:31:51 -07:00
committed by GitHub
parent ff9744fd66
commit 6862b9c745
23 changed files with 1081 additions and 79 deletions

View File

@@ -2844,6 +2844,8 @@ pub struct TurnContextItem {
pub sandbox_policy: SandboxPolicy,
#[serde(skip_serializing_if = "Option::is_none")]
pub network: Option<TurnContextNetworkItem>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_system_sandbox_policy: Option<FileSystemSandboxPolicy>,
pub model: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub personality: Option<Personality>,
@@ -4974,6 +4976,7 @@ mod tests {
assert_eq!(item.trace_id, None);
assert_eq!(item.network, None);
assert_eq!(item.file_system_sandbox_policy, None);
Ok(())
}
@@ -4991,6 +4994,14 @@ mod tests {
allowed_domains: vec!["api.example.com".to_string()],
denied_domains: vec!["blocked.example.com".to_string()],
}),
file_system_sandbox_policy: Some(FileSystemSandboxPolicy::restricted(vec![
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "/tmp/private/**/*.txt".to_string(),
},
access: FileSystemAccessMode::None,
},
])),
model: "gpt-5".to_string(),
personality: None,
collaboration_mode: None,
@@ -5011,6 +5022,19 @@ mod tests {
"denied_domains": ["blocked.example.com"],
})
);
assert_eq!(
value["file_system_sandbox_policy"],
json!({
"kind": "restricted",
"entries": [{
"path": {
"type": "glob_pattern",
"pattern": "/tmp/private/**/*.txt"
},
"access": "none"
}]
})
);
Ok(())
}