mirror of
https://github.com/openai/codex.git
synced 2026-10-19 17:28:46 +00:00
Keep step settings and approval environments consistent (#46556)
## Why Background processes can outlive their launching turn. Their approval reviews need the originating environment's filesystem restrictions, even when the active turn uses a different environment. Remote restrictions must also be resolved using executor paths rather than the local filesystem. ## What changed - Capture settings and environment selections together in `StepInputs`. Preserve the captured selection during active model updates while allowing environment startup to finish. - Carry environment IDs into Guardian network and `execve` requests, and use the owning process's environment with current review settings. Refresh environment readiness for network requests that fall back to the active turn. - Resolve remote denied paths and globs using the executor's policy context. Decline automatic approval when the request's environment is unavailable or an explicitly denied temporary directory cannot be resolved. ## Testing Add regression coverage for active model updates retaining their environment while the next turn adopts a new selection, background approvals using their owning environment, newly ready network environments, and remote denied paths and globs, including Windows paths and missing temporary-directory metadata. GitOrigin-RevId: b620d4b030c597a4f430fe0500864fa5469db3d4
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
//! Executor-context policy operations that never consult controller paths or environment.
|
||||
|
||||
use super::FileSystemAccessMode;
|
||||
use super::FileSystemPath;
|
||||
use super::FileSystemSandboxKind;
|
||||
use super::FileSystemSandboxPolicy;
|
||||
use super::FileSystemSandboxPolicyContext;
|
||||
use super::FileSystemSpecialPath;
|
||||
@@ -8,8 +10,10 @@ use super::InvalidDenyReadGlobBehavior;
|
||||
use super::ReadDenyMatcher;
|
||||
use super::deny_read_validator::DenyReadValidator;
|
||||
use super::deny_read_validator::has_context_read_denials;
|
||||
use super::file_system_root;
|
||||
use codex_utils_path_uri::PathConvention;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use std::collections::HashSet;
|
||||
|
||||
const INVALID_PATH: &str = "managed filesystem denial cannot be materialized safely";
|
||||
const SHADOWED: &str = "managed filesystem denial is shadowed by a selected profile grant";
|
||||
@@ -73,6 +77,45 @@ impl FileSystemSandboxPolicy {
|
||||
.with_materialized_project_roots_for_path_uris(roots)
|
||||
}
|
||||
|
||||
/// Returns explicitly denied roots using only the selected executor's paths.
|
||||
/// A filesystem-root denial is omitted so it does not obscure narrower readable paths.
|
||||
/// Fails rather than omit a denied temporary directory the executor did not report.
|
||||
pub fn get_unreadable_roots_with_context(
|
||||
&self,
|
||||
context: &FileSystemSandboxPolicyContext<'_>,
|
||||
) -> Result<Vec<PathUri>, String> {
|
||||
if !matches!(self.kind, FileSystemSandboxKind::Restricted) {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
if context.temporary_directories.is_none()
|
||||
&& self.entries.iter().any(|entry| {
|
||||
entry.access == FileSystemAccessMode::Deny
|
||||
&& matches!(
|
||||
&entry.path,
|
||||
FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::Tmpdir
|
||||
}
|
||||
)
|
||||
})
|
||||
{
|
||||
return Err("executor did not report its denied temporary directories".into());
|
||||
}
|
||||
let filesystem_root = file_system_root(context);
|
||||
let mut seen = HashSet::new();
|
||||
Ok(self
|
||||
.resolved_entries(context)
|
||||
.into_iter()
|
||||
.filter(|(path, access)| {
|
||||
*access == FileSystemAccessMode::Deny
|
||||
&& !filesystem_root
|
||||
.as_ref()
|
||||
.is_some_and(|root| path.starts_with(root) && root.starts_with(path))
|
||||
&& seen.insert(path.clone())
|
||||
})
|
||||
.map(|(path, _)| path)
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Resolves deny glob text using the execution host's convention and home.
|
||||
pub fn get_unreadable_globs_with_context(
|
||||
&self,
|
||||
|
||||
@@ -43,6 +43,10 @@ fn read_denials_use_target_paths_and_native_glob_semantics() {
|
||||
let matcher = ReadDenyMatcher::try_new_with_context(&policy, &context)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
policy.get_unreadable_roots_with_context(&context),
|
||||
Ok(vec![cwd.join("private").unwrap()]),
|
||||
);
|
||||
for (path, expected) in [
|
||||
("private", true),
|
||||
("private/file", true),
|
||||
@@ -208,6 +212,17 @@ fn malformed_or_unresolvable_denials_fail_closed() {
|
||||
path: uri("file:///C:/private"),
|
||||
})]);
|
||||
assert!(ReadDenyMatcher::try_new_with_context(&policy, &context).is_err());
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![deny(FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::Tmpdir,
|
||||
})]);
|
||||
assert!(
|
||||
policy
|
||||
.get_unreadable_roots_with_context(&FileSystemSandboxPolicyContext {
|
||||
temporary_directories: None,
|
||||
..context
|
||||
})
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user