From 673fe637cf8a4cc3c8b6d0365216155e59448587 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Fri, 6 Feb 2026 18:52:31 -0800 Subject: [PATCH] refactor(core): preserve network requirements as constraints metadata --- codex-rs/core/src/config/mod.rs | 1 + .../src/config_loader/config_requirements.rs | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 9d7d9195cf..238028c3ba 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1653,6 +1653,7 @@ impl Config { mcp_servers, exec_policy: _, enforce_residency, + network: _network_requirements, } = requirements; apply_requirement_constrained_value( diff --git a/codex-rs/core/src/config_loader/config_requirements.rs b/codex-rs/core/src/config_loader/config_requirements.rs index da475bd4ed..ec654a456e 100644 --- a/codex-rs/core/src/config_loader/config_requirements.rs +++ b/codex-rs/core/src/config_loader/config_requirements.rs @@ -81,6 +81,7 @@ pub struct ConfigRequirements { pub mcp_servers: Option>>, pub(crate) exec_policy: Option>, pub enforce_residency: ConstrainedWithSource>, + pub network: Option>, } impl Default for ConfigRequirements { @@ -101,6 +102,7 @@ impl Default for ConfigRequirements { mcp_servers: None, exec_policy: None, enforce_residency: ConstrainedWithSource::new(Constrained::allow_any(None), None), + network: None, } } } @@ -123,6 +125,20 @@ pub struct McpServerRequirement { pub identity: McpServerIdentity, } +#[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq)] +pub struct NetworkRequirementsToml { + pub enabled: Option, + pub http_port: Option, + pub socks_port: Option, + pub allow_upstream_proxy: Option, + pub dangerously_allow_non_loopback_proxy: Option, + pub dangerously_allow_non_loopback_admin: Option, + pub allowed_domains: Option>, + pub denied_domains: Option>, + pub allow_unix_sockets: Option>, + pub allow_local_binding: Option, +} + #[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(rename_all = "lowercase")] pub enum WebSearchModeRequirement { @@ -170,6 +186,7 @@ pub struct ConfigRequirementsToml { pub mcp_servers: Option>, pub rules: Option, pub enforce_residency: Option, + pub network: Option, } /// Value paired with the requirement source it came from, for better error @@ -202,6 +219,7 @@ pub struct ConfigRequirementsWithSources { pub mcp_servers: Option>>, pub rules: Option>, pub enforce_residency: Option>, + pub network: Option>, } impl ConfigRequirementsWithSources { @@ -236,6 +254,7 @@ impl ConfigRequirementsWithSources { mcp_servers, rules, enforce_residency, + network, } ); } @@ -248,6 +267,7 @@ impl ConfigRequirementsWithSources { mcp_servers, rules, enforce_residency, + network, } = self; ConfigRequirementsToml { allowed_approval_policies: allowed_approval_policies.map(|sourced| sourced.value), @@ -256,6 +276,7 @@ impl ConfigRequirementsWithSources { mcp_servers: mcp_servers.map(|sourced| sourced.value), rules: rules.map(|sourced| sourced.value), enforce_residency: enforce_residency.map(|sourced| sourced.value), + network: network.map(|sourced| sourced.value), } } } @@ -301,6 +322,7 @@ impl ConfigRequirementsToml { && self.mcp_servers.is_none() && self.rules.is_none() && self.enforce_residency.is_none() + && self.network.is_none() } } @@ -315,6 +337,7 @@ impl TryFrom for ConfigRequirements { mcp_servers, rules, enforce_residency, + network, } = toml; let approval_policy = match allowed_approval_policies { @@ -478,6 +501,7 @@ impl TryFrom for ConfigRequirements { mcp_servers, exec_policy, enforce_residency, + network, }) } } @@ -506,6 +530,7 @@ mod tests { mcp_servers, rules, enforce_residency, + network, } = toml; ConfigRequirementsWithSources { allowed_approval_policies: allowed_approval_policies @@ -518,6 +543,7 @@ mod tests { rules: rules.map(|value| Sourced::new(value, RequirementSource::Unknown)), enforce_residency: enforce_residency .map(|value| Sourced::new(value, RequirementSource::Unknown)), + network: network.map(|value| Sourced::new(value, RequirementSource::Unknown)), } } @@ -547,6 +573,7 @@ mod tests { mcp_servers: None, rules: None, enforce_residency: Some(enforce_residency), + network: None, }; target.merge_unset_fields(source.clone(), other); @@ -566,6 +593,7 @@ mod tests { mcp_servers: None, rules: None, enforce_residency: Some(Sourced::new(enforce_residency, enforce_source)), + network: None, } ); } @@ -597,6 +625,7 @@ mod tests { mcp_servers: None, rules: None, enforce_residency: None, + network: None, } ); Ok(()) @@ -636,6 +665,7 @@ mod tests { mcp_servers: None, rules: None, enforce_residency: None, + network: None, } ); Ok(()) @@ -951,6 +981,53 @@ mod tests { Ok(()) } + #[test] + fn network_requirements_are_preserved_as_constraints_with_source() -> Result<()> { + let toml_str = r#" + [network] + enabled = true + allow_upstream_proxy = false + allowed_domains = ["api.example.com", "*.openai.com"] + denied_domains = ["blocked.example.com"] + allow_unix_sockets = ["/tmp/example.sock"] + allow_local_binding = false + "#; + + let source = RequirementSource::CloudRequirements; + let mut requirements_with_sources = ConfigRequirementsWithSources::default(); + requirements_with_sources.merge_unset_fields(source.clone(), from_str(toml_str)?); + + let requirements = ConfigRequirements::try_from(requirements_with_sources)?; + let sourced_network = requirements + .network + .expect("network requirements should be preserved as constraints"); + + assert_eq!(sourced_network.source, source); + assert_eq!(sourced_network.value.enabled, Some(true)); + assert_eq!(sourced_network.value.allow_upstream_proxy, Some(false)); + assert_eq!( + sourced_network.value.allowed_domains.as_ref(), + Some(&vec![ + "api.example.com".to_string(), + "*.openai.com".to_string() + ]) + ); + assert_eq!( + sourced_network.value.denied_domains.as_ref(), + Some(&vec!["blocked.example.com".to_string()]) + ); + assert_eq!( + sourced_network.value.allow_unix_sockets.as_ref(), + Some(&vec!["/tmp/example.sock".to_string()]) + ); + assert_eq!( + sourced_network.value.allow_local_binding, + Some(false) + ); + + Ok(()) + } + #[test] fn deserialize_mcp_server_requirements() -> Result<()> { let toml_str = r#"