102 lines
3.0 KiB
Rust
102 lines
3.0 KiB
Rust
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());
|
|
}
|