Enforce application network policy in standalone MCP commands (#52215)

## Why

Standalone MCP commands must honor `application.network` restrictions during OAuth discovery and login, even when the MCP server is allowed by its identity requirements.

## What changed

- Apply local network policy to cloud configuration bootstrap authentication and activate the effective policy on the loaded CLI configuration.
- Load `codex mcp add` through the shared cloud configuration loader and use its resolved `codex_home` when saving server configuration.
- Block disallowed OAuth discovery requests before connecting while preserving the ability to save a server when discovery is unavailable.

## Testing

Add isolated regression tests for blocked MCP discovery and login, discovery with network policy disabled, allowed and denied HTTPS destinations, and rejection of malformed managed network policy. Connection checks verify that denied requests never reach a TCP listener.

GitOrigin-RevId: f91401b51d6e6b9472bb01838f0d0903f83d62f3
This commit is contained in:
lsingh-oai
2026-10-08 18:22:39 +00:00
committed by copyberry
parent a645742bb5
commit 79773e8826
3 changed files with 301 additions and 18 deletions
+10 -4
View File
@@ -1,5 +1,6 @@
use anyhow::Context;
use anyhow::Result;
use codex_app_server::in_process::EmbeddedNetworkPolicy;
use codex_cloud_config::cloud_config_bundle_loader_for_storage;
use codex_config::CloudConfigBundleLoader;
use codex_config::ConfigLoadOptions;
@@ -17,7 +18,7 @@ pub(crate) async fn load_config(
config_overrides: &CliConfigOverrides,
loader_overrides: LoaderOverrides,
) -> Result<Config> {
config_builder(
let mut config = config_builder(
config_overrides,
loader_overrides,
ConfigOverrides::default(),
@@ -25,7 +26,9 @@ pub(crate) async fn load_config(
.await?
.build()
.await
.context("failed to load configuration")
.context("failed to load configuration")?;
EmbeddedNetworkPolicy::default().activate(&mut config);
Ok(config)
}
pub(crate) async fn config_builder(
@@ -33,6 +36,7 @@ pub(crate) async fn config_builder(
loader_overrides: LoaderOverrides,
harness_overrides: ConfigOverrides,
) -> Result<ConfigBuilder> {
let network_policy = EmbeddedNetworkPolicy::load(&loader_overrides).await;
let cli_overrides = config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?;
@@ -55,8 +59,10 @@ pub(crate) async fn config_builder(
.await
.context("failed to load bootstrap configuration")?;
let cloud_config_bundle = cloud_config_bundle_loader_for_storage(
bootstrap_auth_config(codex_home.as_path(), &bootstrap_config)
.context("failed to resolve cloud configuration authentication")?,
network_policy.bind_bootstrap_auth(
bootstrap_auth_config(codex_home.as_path(), &bootstrap_config)
.context("failed to resolve cloud configuration authentication")?,
),
/*enable_codex_api_key_env*/ false,
)
.await