mirror of
https://github.com/openai/codex.git
synced 2026-05-15 08:42:34 +00:00
Use Windows metadata creation monitor
This commit is contained in:
@@ -122,17 +122,17 @@ pub(crate) struct WindowsProtectedMetadataTarget {
|
||||
}
|
||||
|
||||
/// Layer: Windows adapter layer. The enforcement layer needs to know whether a
|
||||
/// protected metadata path already exists or must be denied before the command
|
||||
/// can create it.
|
||||
/// protected metadata path already exists, or whether the sandbox should watch
|
||||
/// the parent for a command-created metadata object.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum WindowsProtectedMetadataMode {
|
||||
/// The protected metadata object exists before launch, so the Windows
|
||||
/// sandbox should deny writes to the object and any canonical target.
|
||||
ExistingDeny,
|
||||
/// The protected metadata object is absent before launch, so the Windows
|
||||
/// sandbox should create and deny-list a temporary sentinel before command
|
||||
/// execution can begin.
|
||||
MissingDenySentinel,
|
||||
/// sandbox should listen for filesystem creation events and remove any
|
||||
/// command-created object before reporting the command as denied.
|
||||
MissingCreationMonitor,
|
||||
}
|
||||
|
||||
fn windows_sandbox_uses_elevated_backend(
|
||||
@@ -671,8 +671,8 @@ async fn exec_windows_sandbox(
|
||||
WindowsProtectedMetadataMode::ExistingDeny => {
|
||||
codex_windows_sandbox::ProtectedMetadataMode::ExistingDeny
|
||||
}
|
||||
WindowsProtectedMetadataMode::MissingDenySentinel => {
|
||||
codex_windows_sandbox::ProtectedMetadataMode::MissingDenySentinel
|
||||
WindowsProtectedMetadataMode::MissingCreationMonitor => {
|
||||
codex_windows_sandbox::ProtectedMetadataMode::MissingCreationMonitor
|
||||
}
|
||||
};
|
||||
codex_windows_sandbox::ProtectedMetadataTarget {
|
||||
@@ -1366,7 +1366,7 @@ fn windows_protected_metadata_mode(path: &AbsolutePathBuf) -> WindowsProtectedMe
|
||||
return WindowsProtectedMetadataMode::ExistingDeny;
|
||||
}
|
||||
|
||||
WindowsProtectedMetadataMode::MissingDenySentinel
|
||||
WindowsProtectedMetadataMode::MissingCreationMonitor
|
||||
}
|
||||
|
||||
fn has_reopened_writable_descendant(
|
||||
|
||||
@@ -666,15 +666,15 @@ fn windows_restricted_token_supports_full_read_split_write_read_carveouts() {
|
||||
protected_metadata_targets: vec![
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".agents"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".codex"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".git"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
],
|
||||
}))
|
||||
@@ -778,15 +778,15 @@ fn windows_elevated_supports_split_write_read_carveouts() {
|
||||
protected_metadata_targets: vec![
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: expected_root.join(".agents"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: expected_root.join(".codex"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: expected_root.join(".git"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
],
|
||||
}))
|
||||
@@ -840,11 +840,11 @@ fn windows_metadata_plan_marks_existing_metadata_for_deny() {
|
||||
protected_metadata_targets: vec![
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".agents"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".codex"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".git"),
|
||||
@@ -856,7 +856,7 @@ fn windows_metadata_plan_marks_existing_metadata_for_deny() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn windows_metadata_plan_uses_sentinel_for_nested_missing_git() {
|
||||
fn windows_metadata_plan_monitors_nested_missing_git() {
|
||||
let temp_dir = tempfile::TempDir::new().expect("tempdir");
|
||||
let repo = dunce::canonicalize(temp_dir.path())
|
||||
.expect("canonical temp dir")
|
||||
@@ -904,15 +904,15 @@ fn windows_metadata_plan_uses_sentinel_for_nested_missing_git() {
|
||||
protected_metadata_targets: vec![
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".agents"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".codex"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".git"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
],
|
||||
}))
|
||||
@@ -973,15 +973,15 @@ fn windows_shell_runtime_path_resolves_metadata_overrides() {
|
||||
vec![
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".agents"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".codex"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".git"),
|
||||
mode: WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
@@ -190,8 +190,8 @@ fn protected_metadata_targets_for_windows_session(
|
||||
WindowsProtectedMetadataMode::ExistingDeny => {
|
||||
codex_windows_sandbox::ProtectedMetadataMode::ExistingDeny
|
||||
}
|
||||
WindowsProtectedMetadataMode::MissingDenySentinel => {
|
||||
codex_windows_sandbox::ProtectedMetadataMode::MissingDenySentinel
|
||||
WindowsProtectedMetadataMode::MissingCreationMonitor => {
|
||||
codex_windows_sandbox::ProtectedMetadataMode::MissingCreationMonitor
|
||||
}
|
||||
};
|
||||
codex_windows_sandbox::ProtectedMetadataTarget {
|
||||
|
||||
@@ -183,15 +183,15 @@ fn open_session_prepares_windows_metadata_overrides_for_unified_exec() {
|
||||
vec![
|
||||
crate::exec::WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".agents"),
|
||||
mode: crate::exec::WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: crate::exec::WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
crate::exec::WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".codex"),
|
||||
mode: crate::exec::WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: crate::exec::WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
crate::exec::WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".git"),
|
||||
mode: crate::exec::WindowsProtectedMetadataMode::MissingDenySentinel,
|
||||
mode: crate::exec::WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
@@ -851,11 +851,17 @@ fn build_payload_deny_write_paths(
|
||||
request.command_cwd,
|
||||
request.env_map,
|
||||
);
|
||||
// Sentinel targets are protected by the dedicated metadata payload so setup
|
||||
// applies a direct deny ACE without inheriting that deny into descendants.
|
||||
let sentinel_path_keys: HashSet<String> = protected_metadata_targets
|
||||
// Missing metadata targets are protected by the dedicated metadata payload.
|
||||
// Do not let generic deny-write setup materialize them as directories.
|
||||
let missing_metadata_path_keys: HashSet<String> = protected_metadata_targets
|
||||
.iter()
|
||||
.filter(|target| target.mode == ProtectedMetadataMode::MissingDenySentinel)
|
||||
.filter(|target| {
|
||||
matches!(
|
||||
target.mode,
|
||||
ProtectedMetadataMode::MissingCreationMonitor
|
||||
| ProtectedMetadataMode::MissingDenySentinel
|
||||
)
|
||||
})
|
||||
.map(|target| canonical_path_key(&target.path))
|
||||
.collect();
|
||||
let mut deny_write_paths: Vec<PathBuf> = explicit_deny_write_paths
|
||||
@@ -870,7 +876,7 @@ fn build_payload_deny_write_paths(
|
||||
})
|
||||
.collect();
|
||||
deny_write_paths.extend(allow_deny_paths.deny);
|
||||
deny_write_paths.retain(|path| !sentinel_path_keys.contains(&canonical_path_key(path)));
|
||||
deny_write_paths.retain(|path| !missing_metadata_path_keys.contains(&canonical_path_key(path)));
|
||||
deny_write_paths
|
||||
}
|
||||
|
||||
@@ -1504,13 +1510,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payload_deny_write_paths_skip_missing_metadata_sentinels() {
|
||||
fn payload_deny_write_paths_skip_missing_metadata_targets() {
|
||||
let tmp = TempDir::new().expect("tempdir");
|
||||
let codex_home = tmp.path().join("codex-home");
|
||||
let command_cwd = tmp.path().join("workspace");
|
||||
let command_git = command_cwd.join(".git");
|
||||
let command_codex = command_cwd.join(".codex");
|
||||
let explicit_deny = tmp.path().join("explicit-deny");
|
||||
fs::create_dir_all(&command_git).expect("create command .git sentinel");
|
||||
fs::create_dir_all(&command_cwd).expect("create workspace");
|
||||
let policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
network_access: false,
|
||||
@@ -1529,10 +1536,16 @@ mod tests {
|
||||
let deny_write_paths = super::build_payload_deny_write_paths(
|
||||
&request,
|
||||
Some(vec![explicit_deny.clone()]),
|
||||
&[super::ProtectedMetadataTarget {
|
||||
path: command_git.clone(),
|
||||
mode: super::ProtectedMetadataMode::MissingDenySentinel,
|
||||
}],
|
||||
&[
|
||||
super::ProtectedMetadataTarget {
|
||||
path: command_git,
|
||||
mode: super::ProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
super::ProtectedMetadataTarget {
|
||||
path: command_codex,
|
||||
mode: super::ProtectedMetadataMode::MissingDenySentinel,
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
assert_eq!(vec![explicit_deny], deny_write_paths);
|
||||
|
||||
Reference in New Issue
Block a user