fix(sandbox): writable roots Codex dérivées des permissions effectives du menu agent — #197 (QA verte)
This commit is contained in:
101
crates/application/src/codex_permissions.rs
Normal file
101
crates/application/src/codex_permissions.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use domain::permission::{Capability, Effect, EffectivePermissions, Posture};
|
||||
use domain::ports::StructuredProviderLaunchPolicy;
|
||||
use domain::sandbox::{compile_sandbox_plan, PathAccess, SandboxContext};
|
||||
use domain::NetworkPolicy;
|
||||
|
||||
pub(crate) fn compile_codex_launch_policy(
|
||||
project_root: &str,
|
||||
run_dir: &str,
|
||||
permissions: Option<&EffectivePermissions>,
|
||||
network: Option<NetworkPolicy>,
|
||||
extra_writable_roots: &[String],
|
||||
) -> StructuredProviderLaunchPolicy {
|
||||
let mut writable_roots = codex_permission_writable_roots(project_root, run_dir, permissions);
|
||||
append_unique_paths(&mut writable_roots, extra_writable_roots);
|
||||
let sandbox_mode = codex_sandbox_mode(permissions, &writable_roots).to_owned();
|
||||
if sandbox_mode != "workspace-write" {
|
||||
writable_roots.clear();
|
||||
}
|
||||
|
||||
StructuredProviderLaunchPolicy::Codex {
|
||||
sandbox_mode,
|
||||
writable_roots,
|
||||
network_access: codex_network_access(network),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn codex_permission_writable_roots(
|
||||
project_root: &str,
|
||||
run_dir: &str,
|
||||
permissions: Option<&EffectivePermissions>,
|
||||
) -> Vec<String> {
|
||||
let Some(permissions) = permissions else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let mut roots = Vec::new();
|
||||
if !has_rw_deny(permissions)
|
||||
&& permissions.fallback() != Posture::Deny
|
||||
&& permissions.decide_file(Capability::Write, "") != Posture::Deny
|
||||
{
|
||||
roots.push(project_root.to_owned());
|
||||
}
|
||||
|
||||
if let Some(plan) = compile_sandbox_plan(
|
||||
Some(permissions),
|
||||
&SandboxContext {
|
||||
project_root,
|
||||
run_dir,
|
||||
},
|
||||
) {
|
||||
for grant in plan
|
||||
.allowed
|
||||
.iter()
|
||||
.filter(|grant| grant.access.contains(PathAccess::RW))
|
||||
{
|
||||
append_unique_path(&mut roots, &grant.abs_root);
|
||||
}
|
||||
}
|
||||
roots.sort();
|
||||
roots
|
||||
}
|
||||
|
||||
pub(crate) fn codex_sandbox_mode(
|
||||
permissions: Option<&EffectivePermissions>,
|
||||
writable_roots: &[String],
|
||||
) -> &'static str {
|
||||
if !writable_roots.is_empty() {
|
||||
return "workspace-write";
|
||||
}
|
||||
if permissions.is_some_and(has_rw_deny) {
|
||||
return "read-only";
|
||||
}
|
||||
match permissions.map(EffectivePermissions::fallback) {
|
||||
Some(Posture::Ask | Posture::Allow) | None => "workspace-write",
|
||||
Some(Posture::Deny) => "read-only",
|
||||
}
|
||||
}
|
||||
|
||||
fn has_rw_deny(permissions: &EffectivePermissions) -> bool {
|
||||
permissions.rules().iter().any(|rule| {
|
||||
rule.effect() == Effect::Deny
|
||||
&& matches!(rule.capability(), Capability::Write | Capability::Delete)
|
||||
})
|
||||
}
|
||||
|
||||
fn codex_network_access(network: Option<NetworkPolicy>) -> bool {
|
||||
matches!(network, Some(NetworkPolicy::Allow))
|
||||
}
|
||||
|
||||
fn append_unique_paths(target: &mut Vec<String>, roots: &[String]) {
|
||||
for root in roots {
|
||||
append_unique_path(target, root);
|
||||
}
|
||||
}
|
||||
|
||||
fn append_unique_path(target: &mut Vec<String>, root: &str) {
|
||||
if root.is_empty() || target.iter().any(|existing| existing == root) {
|
||||
return;
|
||||
}
|
||||
target.push(root.to_owned());
|
||||
}
|
||||
Reference in New Issue
Block a user