Files
codex/codex-rs/windows-sandbox-service/src/machine_policy_tests.rs
T
viyatb-oai dfcb5b5994 Support managed network requirements under permissions.network (#58182)
## What changed

Allow shared network constraints in `[permissions.network]` in `requirements.toml`, alongside `[permissions.filesystem]`. Apply them throughout configuration loading, network approvals, Windows sandbox policy validation, and `configRequirements/read`, and show their source in `/debug-config`.

Retain `[network_proxy]` and `[experimental_network]`, but reject mixing either with `[permissions.network]`, even across requirements layers. Preserve independent network and filesystem precedence and source attribution, and report unknown nested network fields.

Reserve `network` as a managed requirements table name and reject empty or profile-shaped tables with migration guidance. Profiles named `network` remain valid in `config.toml`. Document that policy publishers must keep using `[experimental_network]` until all readers support the chosen replacement.

## Testing

Add coverage for layered constraints and source attribution, conflicting spellings, profile-name migration errors, unknown-field diagnostics, and cloud validation. Extend API, project discovery, network approval, and Windows policy tests to cover `[permissions.network]`, and verify managed proxy settings remain independent of disabled feature configuration.

GitOrigin-RevId: 994dfca9abf5a7aea914276c0ded8b40d7ed0372
2026-10-29 22:36:06 +00:00

335 lines
10 KiB
Rust

use anyhow::Result;
use codex_config::ConfigRequirements;
use codex_config::ConfigRequirementsWithSources;
use codex_config::RequirementSource;
use codex_windows_sandbox::WindowsSandboxProvisioningSettings;
use codex_windows_sandbox::WindowsSandboxProxyListeners;
use pretty_assertions::assert_eq;
use crate::ipc::ProvisioningRequest;
const NETWORK_TABLES: [&str; 3] = [
"network_proxy",
"experimental_network",
"permissions.network",
];
fn validate_requirements(
settings: &WindowsSandboxProvisioningSettings,
listeners: WindowsSandboxProxyListeners,
contents: &str,
) -> Result<()> {
let mut requirements_with_sources = ConfigRequirementsWithSources::default();
requirements_with_sources
.merge_unset_fields(RequirementSource::Unknown, toml::from_str(contents)?);
let requirements = ConfigRequirements::try_from(requirements_with_sources)?;
super::validate_requirements(settings, &listeners, &requirements)
}
#[test]
fn only_registered_refresh_skips_configuration_loading() {
for (registered_core, refresh_only) in
[(true, true), (true, false), (false, false), (false, true)]
{
let request = ProvisioningRequest {
codex_home: std::env::temp_dir(),
registered_core,
refresh_only,
settings: WindowsSandboxProvisioningSettings::default(),
listeners: WindowsSandboxProxyListeners::default(),
};
// An invalid worker token makes any attempt to load config fail closed.
// Refresh must succeed without starting that runtime; setup must not.
let result = super::validate_provisioning_request(
&request.codex_home,
&request,
std::ptr::null_mut(),
);
assert_eq!(result.is_ok(), registered_core && refresh_only);
if let Err(error) = result {
assert!(error.to_string().contains("failed to impersonate"));
}
}
}
#[test]
fn unmanaged_policy_allows_default_and_requested_network_settings() -> Result<()> {
validate_requirements(
&WindowsSandboxProvisioningSettings::default(),
WindowsSandboxProxyListeners::default(),
"",
)?;
validate_requirements(
&WindowsSandboxProvisioningSettings {
proxy_ports: vec![3128, 8080, 9000],
allow_local_binding: true,
},
WindowsSandboxProxyListeners {
http_ports: vec![3128],
socks_ports: vec![8080],
},
"allowed_sandbox_modes = [\"read-only\", \"workspace-write\"]",
)
}
#[test]
fn elevated_sandbox_is_allowed_when_included() -> Result<()> {
validate_requirements(
&WindowsSandboxProvisioningSettings::default(),
WindowsSandboxProxyListeners::default(),
"[windows]\nallowed_sandbox_implementations = [\"unelevated\", \"elevated\"]",
)
}
#[test]
fn elevated_sandbox_is_rejected_when_prohibited_or_empty() {
for implementations in ["[\"unelevated\"]", "[]"] {
let policy = format!("[windows]\nallowed_sandbox_implementations = {implementations}");
assert!(
validate_requirements(
&WindowsSandboxProvisioningSettings::default(),
WindowsSandboxProxyListeners::default(),
&policy,
)
.is_err()
);
}
}
#[test]
fn forbidden_local_binding_is_rejected() {
let settings = WindowsSandboxProvisioningSettings {
proxy_ports: Vec::new(),
allow_local_binding: true,
};
for table in NETWORK_TABLES {
let policy = format!("[{table}]\nallow_local_binding = false");
let error =
validate_requirements(&settings, WindowsSandboxProxyListeners::default(), &policy)
.expect_err("local binding must be rejected");
assert_eq!(
error.to_string(),
"managed policy does not permit local network binding",
"{table}"
);
}
}
#[test]
fn disabled_network_rejects_proxy_ports_and_local_binding() -> Result<()> {
for table in NETWORK_TABLES {
let policy = format!("[{table}]\nenabled = false");
validate_requirements(
&WindowsSandboxProvisioningSettings::default(),
WindowsSandboxProxyListeners::default(),
&policy,
)?;
for settings in [
WindowsSandboxProvisioningSettings {
proxy_ports: vec![3128],
allow_local_binding: false,
},
WindowsSandboxProvisioningSettings {
proxy_ports: Vec::new(),
allow_local_binding: true,
},
] {
assert!(
validate_requirements(&settings, WindowsSandboxProxyListeners::default(), &policy)
.is_err(),
"{table}"
);
}
}
Ok(())
}
#[test]
fn enabled_network_allows_unrestricted_proxy_ports() -> Result<()> {
validate_requirements(
&WindowsSandboxProvisioningSettings {
proxy_ports: vec![3128, 8080, 8081, 9000],
allow_local_binding: false,
},
WindowsSandboxProxyListeners {
http_ports: vec![3128, 8080],
socks_ports: vec![8081],
},
"[experimental_network]\nenabled = true",
)
}
#[test]
fn disabled_local_binding_is_allowed_when_policy_permits_binding() -> Result<()> {
validate_requirements(
&WindowsSandboxProvisioningSettings::default(),
WindowsSandboxProxyListeners::default(),
"[experimental_network]\nallow_local_binding = true",
)
}
#[test]
fn managed_proxy_ports_allow_only_configured_ports() -> Result<()> {
let allowed = WindowsSandboxProvisioningSettings {
proxy_ports: vec![1080, 3128],
allow_local_binding: false,
};
let prohibited = WindowsSandboxProvisioningSettings {
proxy_ports: vec![1080, 3128, 8080],
allow_local_binding: false,
};
for table in NETWORK_TABLES {
let policy = format!("[{table}]\nhttp_port = 3128\nsocks_port = 1080");
for listeners in [
WindowsSandboxProxyListeners {
http_ports: vec![3128],
socks_ports: vec![1080],
},
WindowsSandboxProxyListeners::default(),
] {
validate_requirements(&allowed, listeners, &policy)?;
}
for listeners in [
WindowsSandboxProxyListeners {
http_ports: vec![3128, 8080],
socks_ports: vec![1080],
},
WindowsSandboxProxyListeners {
http_ports: vec![3128],
socks_ports: vec![1080, 8080],
},
WindowsSandboxProxyListeners {
http_ports: vec![3128],
socks_ports: vec![1080],
},
WindowsSandboxProxyListeners::default(),
] {
assert!(
validate_requirements(&prohibited, listeners, &policy).is_err(),
"{table}"
);
}
validate_requirements(
&WindowsSandboxProvisioningSettings::default(),
WindowsSandboxProxyListeners::default(),
&policy,
)?;
}
Ok(())
}
#[test]
fn managed_http_port_does_not_restrict_the_unmanaged_socks_listener() -> Result<()> {
validate_requirements(
&WindowsSandboxProvisioningSettings {
proxy_ports: vec![3128, 8081],
allow_local_binding: false,
},
WindowsSandboxProxyListeners {
http_ports: vec![3128],
socks_ports: vec![8081],
},
"[experimental_network]\nhttp_port = 3128",
)
}
#[test]
fn managed_socks_port_does_not_restrict_the_unmanaged_http_listener() -> Result<()> {
validate_requirements(
&WindowsSandboxProvisioningSettings {
proxy_ports: vec![1080, 3128],
allow_local_binding: false,
},
WindowsSandboxProxyListeners {
http_ports: vec![3128],
socks_ports: vec![1080],
},
"[experimental_network]\nsocks_port = 1080",
)
}
#[test]
fn managed_proxy_ports_reject_swapped_listener_roles() {
assert!(
validate_requirements(
&WindowsSandboxProvisioningSettings {
proxy_ports: vec![1080, 3128],
allow_local_binding: false,
},
WindowsSandboxProxyListeners {
http_ports: vec![1080],
socks_ports: vec![3128],
},
"[experimental_network]\nhttp_port = 3128\nsocks_port = 1080",
)
.is_err()
);
}
#[test]
fn managed_socks_port_rejects_a_mismatched_socks_listener() {
assert!(
validate_requirements(
&WindowsSandboxProvisioningSettings {
proxy_ports: vec![1080, 3128, 8081],
allow_local_binding: false,
},
WindowsSandboxProxyListeners {
http_ports: vec![3128],
socks_ports: vec![1080, 8081],
},
"[experimental_network]\nhttp_port = 3128\nsocks_port = 1080",
)
.is_err()
);
}
#[test]
fn managed_socks_port_does_not_require_a_disabled_socks_listener() -> Result<()> {
validate_requirements(
&WindowsSandboxProvisioningSettings {
proxy_ports: vec![3128],
allow_local_binding: false,
},
WindowsSandboxProxyListeners {
http_ports: vec![3128],
socks_ports: Vec::new(),
},
"[experimental_network]\nhttp_port = 3128\nsocks_port = 1080",
)
}
#[test]
fn malformed_machine_policy_fails_closed() {
for table in NETWORK_TABLES {
let policy = format!("[{table}]\nhttp_port = \"invalid\"");
assert!(
validate_requirements(
&WindowsSandboxProvisioningSettings::default(),
WindowsSandboxProxyListeners::default(),
&policy,
)
.is_err(),
"{table}"
);
}
for table in ["network_proxy", "experimental_network"] {
assert!(
validate_requirements(
&WindowsSandboxProvisioningSettings::default(),
WindowsSandboxProxyListeners::default(),
&format!("[{table}]\nenabled = true\n[permissions.network]\nenabled = true")
)
.is_err()
);
}
assert!(
validate_requirements(
&WindowsSandboxProvisioningSettings::default(),
WindowsSandboxProxyListeners::default(),
"[windows]\nallowed_sandbox_implementations = [\"invalid\"]"
)
.is_err()
);
}