Codex Handbook
core/src/network_proxy_loader_tests.rs 658 lines
use super::*;use codex_app_server_protocol::ConfigLayerSource;use codex_config::ConfigLayerEntry;use codex_config::ConfigLayerStack;use codex_config::ConfigRequirements;use codex_config::ConfigRequirementsToml;use codex_config::permissions_toml::NetworkDomainPermissionToml;use codex_config::permissions_toml::NetworkDomainPermissionsToml;use codex_execpolicy::Decision;use codex_execpolicy::NetworkRuleProtocol;use codex_execpolicy::Policy;use pretty_assertions::assert_eq;use std::collections::BTreeMap;#[test]fn higher_precedence_profile_network_overlays_domain_entries() {    let lower_network: toml::Value = toml::from_str(        r#"default_permissions = "dev"[permissions.dev.network][permissions.dev.network.domains]"lower.example.com" = "allow""blocked.example.com" = "deny""#,    )    .expect("lower layer should parse");    let higher_network: toml::Value = toml::from_str(        r#"default_permissions = "dev"[permissions.dev.network][permissions.dev.network.domains]"higher.example.com" = "allow""#,    )    .expect("higher layer should parse");    let mut config = NetworkProxyConfig::default();    apply_network_tables(        &mut config,        network_tables_from_toml(&lower_network).expect("lower layer should deserialize"),    )    .expect("lower layer should apply");    apply_network_tables(        &mut config,        network_tables_from_toml(&higher_network).expect("higher layer should deserialize"),    )    .expect("higher layer should apply");    assert_eq!(        config.network.allowed_domains(),        Some(vec![            "lower.example.com".to_string(),            "higher.example.com".to_string()        ])    );    assert_eq!(        config.network.denied_domains(),        Some(vec!["blocked.example.com".to_string()])    );}#[test]fn higher_precedence_profile_network_overrides_matching_domain_entries() {    let lower_network: toml::Value = toml::from_str(        r#"default_permissions = "dev"[permissions.dev.network][permissions.dev.network.domains]"shared.example.com" = "deny""other.example.com" = "allow""#,    )    .expect("lower layer should parse");    let higher_network: toml::Value = toml::from_str(        r#"default_permissions = "dev"[permissions.dev.network][permissions.dev.network.domains]"shared.example.com" = "allow""#,    )    .expect("higher layer should parse");    let mut config = NetworkProxyConfig::default();    apply_network_tables(        &mut config,        network_tables_from_toml(&lower_network).expect("lower layer should deserialize"),    )    .expect("lower layer should apply");    apply_network_tables(        &mut config,        network_tables_from_toml(&higher_network).expect("higher layer should deserialize"),    )    .expect("higher layer should apply");    assert_eq!(        config.network.allowed_domains(),        Some(vec![            "other.example.com".to_string(),            "shared.example.com".to_string()        ])    );    assert_eq!(config.network.denied_domains(), None);}#[test]fn higher_precedence_profile_network_overrides_named_mitm_actions() {    let lower_network: toml::Value = toml::from_str(        r#"default_permissions = "workspace"[permissions.workspace.network]mode = "full"[permissions.workspace.network.domains]"lower.example.com" = "allow"[permissions.workspace.network.mitm.hooks.github_write]host = "api.github.com"methods = ["POST"]path_prefixes = ["/repos/openai/"]action = ["strip_auth"][permissions.workspace.network.mitm.actions.strip_auth]strip_request_headers = ["authorization"]"#,    )    .expect("lower layer should parse");    let higher_network: toml::Value = toml::from_str(        r#"default_permissions = "workspace"[permissions.workspace.network]mode = "full"[permissions.workspace.network.domains]"higher.example.com" = "allow"[permissions.workspace.network.mitm.actions.strip_auth]strip_request_headers = ["x-api-key"]"#,    )    .expect("higher layer should parse");    let mut accumulator = NetworkConfigAccumulator::default();    accumulator        .apply_network_tables(            network_tables_from_toml(&lower_network).expect("lower layer should deserialize"),        )        .expect("lower layer should apply");    accumulator        .apply_network_tables(            network_tables_from_toml(&higher_network).expect("higher layer should deserialize"),        )        .expect("higher layer should apply");    let config = accumulator.finish().expect("merged config should build");    assert_eq!(config.network.mode, codex_network_proxy::NetworkMode::Full);    assert!(config.network.mitm);    assert_eq!(        config.network.allowed_domains(),        Some(vec![            "lower.example.com".to_string(),            "higher.example.com".to_string()        ])    );    assert_eq!(config.network.mitm_hooks.len(), 1);    assert_eq!(config.network.mitm_hooks[0].host, "api.github.com");    assert_eq!(config.network.mitm_hooks[0].matcher.methods, vec!["POST"]);    assert_eq!(        config.network.mitm_hooks[0].actions.strip_request_headers,        vec!["x-api-key"]    );}#[test]fn execpolicy_network_rules_overlay_network_lists() {    let mut config = NetworkProxyConfig::default();    config        .network        .set_allowed_domains(vec!["config.example.com".to_string()]);    config        .network        .set_denied_domains(vec!["blocked.example.com".to_string()]);    let mut exec_policy = Policy::empty();    exec_policy        .add_network_rule(            "blocked.example.com",            NetworkRuleProtocol::Https,            Decision::Allow,            /*justification*/ None,        )        .expect("allow rule should be valid");    exec_policy        .add_network_rule(            "api.example.com",            NetworkRuleProtocol::Http,            Decision::Forbidden,            /*justification*/ None,        )        .expect("deny rule should be valid");    apply_exec_policy_network_rules(&mut config, &exec_policy);    assert_eq!(        config.network.allowed_domains(),        Some(vec![            "config.example.com".to_string(),            "blocked.example.com".to_string()        ])    );    assert_eq!(        config.network.denied_domains(),        Some(vec!["api.example.com".to_string()])    );}#[test]fn apply_network_constraints_includes_allow_all_unix_sockets_flag() {    let config: toml::Value = toml::from_str(        r#"default_permissions = "dev"[permissions.dev.network]dangerously_allow_all_unix_sockets = true"#,    )    .expect("permissions profile should parse");    let network = selected_network_from_tables(        network_tables_from_toml(&config).expect("permissions profile should deserialize"),    )    .expect("permissions profile should select a network table")    .expect("network table should be present");    let mut constraints = NetworkProxyConstraints::default();    apply_network_constraints(network, &mut constraints);    assert_eq!(constraints.dangerously_allow_all_unix_sockets, Some(true));}#[test]fn selected_network_from_tables_ignores_builtin_profile_without_permissions_table() {    let config: toml::Value = toml::from_str(        r#"default_permissions = ":workspace""#,    )    .expect("built-in profile config should parse");    let network = selected_network_from_tables(        network_tables_from_toml(&config).expect("built-in profile config should deserialize"),    )    .expect("built-in profile selection should not require permissions tables");    assert_eq!(network, None);}#[test]fn selected_network_from_tables_rejects_unknown_builtin_profile_without_permissions_table() {    let config: toml::Value = toml::from_str(        r#"default_permissions = ":unknown""#,    )    .expect("unknown built-in config should parse");    let err = selected_network_from_tables(        network_tables_from_toml(&config).expect("unknown built-in config should deserialize"),    )    .expect_err("unknown built-in profile should be rejected");    assert_eq!(        err.to_string(),        "default_permissions refers to unknown built-in profile `:unknown`"    );}#[test]fn selected_network_from_tables_resolves_builtin_workspace_parent() {    let config: toml::Value = toml::from_str(        r#"default_permissions = "dev"[permissions.dev]extends = ":workspace"[permissions.dev.network]enabled = true[permissions.dev.network.domains]"child.example.com" = "allow""#,    )    .expect("dev extension config should parse");    let network = selected_network_from_tables(        network_tables_from_toml(&config).expect("dev extension config should deserialize"),    )    .expect("dev extension should resolve")    .expect("dev extension should expose child network config");    assert_eq!(        network,        NetworkToml {            enabled: Some(true),            domains: Some(NetworkDomainPermissionsToml {                entries: BTreeMap::from([(                    "child.example.com".to_string(),                    NetworkDomainPermissionToml::Allow,                )]),            }),            ..Default::default()        }    );}#[test]fn selected_network_from_tables_resolves_permission_profile_inheritance() {    let config: toml::Value = toml::from_str(        r#"default_permissions = "dev"[permissions.base.network]enabled = truedangerously_allow_all_unix_sockets = true[permissions.base.network.domains]"base.example.com" = "allow""shared.example.com" = "deny"[permissions.dev]extends = "base"[permissions.dev.network]allow_local_binding = true[permissions.dev.network.domains]"child.example.com" = "allow""shared.example.com" = "allow""#,    )    .expect("permissions profiles should parse");    let network = selected_network_from_tables(        network_tables_from_toml(&config).expect("permissions profiles should deserialize"),    )    .expect("permissions profiles should select a network table")    .expect("network table should be present");    assert_eq!(        network,        NetworkToml {            enabled: Some(true),            dangerously_allow_all_unix_sockets: Some(true),            allow_local_binding: Some(true),            domains: Some(NetworkDomainPermissionsToml {                entries: BTreeMap::from([                    (                        "base.example.com".to_string(),                        NetworkDomainPermissionToml::Allow,                    ),                    (                        "child.example.com".to_string(),                        NetworkDomainPermissionToml::Allow,                    ),                    (                        "shared.example.com".to_string(),                        NetworkDomainPermissionToml::Allow,                    ),                ]),            }),            ..Default::default()        }    );}#[test]fn config_from_layers_resolves_inherited_profiles_across_layers() {    let lower_layer = ConfigLayerEntry::new(        ConfigLayerSource::SessionFlags,        toml::toml! {            [permissions.base.network.domains]            "base.example.com" = "allow"        }        .into(),    );    let higher_layer = ConfigLayerEntry::new(        ConfigLayerSource::SessionFlags,        toml::toml! {            default_permissions = "dev"            [permissions.dev]            extends = "base"            [permissions.dev.network.domains]            "child.example.com" = "allow"        }        .into(),    );    let layers = ConfigLayerStack::new(        vec![lower_layer, higher_layer],        ConfigRequirements::default(),        ConfigRequirementsToml::default(),    )    .expect("layer stack should be valid");    let config =        config_from_layers(&layers, &Policy::empty()).expect("inherited profiles should load");    assert_eq!(        config.network.allowed_domains(),        Some(vec![            "base.example.com".to_string(),            "child.example.com".to_string(),        ])    );}#[test]fn config_from_layers_normalizes_profile_network_domains_before_merging_layers() {    let lower_layer = ConfigLayerEntry::new(        ConfigLayerSource::SessionFlags,        toml::toml! {            default_permissions = "dev"            [permissions.dev.network.domains]            "example.com" = "deny"        }        .into(),    );    let higher_layer = ConfigLayerEntry::new(        ConfigLayerSource::SessionFlags,        toml::toml! {            [permissions.dev.network.domains]            "EXAMPLE.COM" = "allow"        }        .into(),    );    let layers = ConfigLayerStack::new(        vec![lower_layer, higher_layer],        ConfigRequirements::default(),        ConfigRequirementsToml::default(),    )    .expect("layer stack should be valid");    let config = config_from_layers(&layers, &Policy::empty())        .expect("network domain layer precedence should load");    assert_eq!(        config.network.allowed_domains(),        Some(vec!["example.com".to_string()])    );    assert_eq!(config.network.denied_domains(), None);}#[test]fn config_from_layers_uses_only_the_final_selected_profile_network() {    let lower_layer = ConfigLayerEntry::new(        ConfigLayerSource::SessionFlags,        toml::toml! {            default_permissions = "dev"            [permissions.dev.network.domains]            "lower.example.com" = "allow"        }        .into(),    );    let higher_layer = ConfigLayerEntry::new(        ConfigLayerSource::SessionFlags,        toml::toml! {            default_permissions = ":workspace"        }        .into(),    );    let layers = ConfigLayerStack::new(        vec![lower_layer, higher_layer],        ConfigRequirements::default(),        ConfigRequirementsToml::default(),    )    .expect("layer stack should be valid");    let config = config_from_layers(&layers, &Policy::empty())        .expect("final built-in profile selection should load");    assert_eq!(config.network.allowed_domains(), None);    assert_eq!(config.network.denied_domains(), None);}#[test]fn trusted_constraints_use_only_the_final_selected_profile_network() {    let lower_layer = ConfigLayerEntry::new(        ConfigLayerSource::System {            file: AbsolutePathBuf::try_from(std::path::PathBuf::from("/tmp/system.toml"))                .expect("system config path should be absolute"),        },        toml::toml! {            default_permissions = "dev"            [permissions.dev.network.domains]            "managed.example.com" = "allow"        }        .into(),    );    let higher_layer = ConfigLayerEntry::new(        ConfigLayerSource::LegacyManagedConfigTomlFromFile {            file: AbsolutePathBuf::try_from(std::path::PathBuf::from("/tmp/managed.toml"))                .expect("managed config path should be absolute"),        },        toml::toml! {            default_permissions = ":workspace"        }        .into(),    );    let layers = ConfigLayerStack::new(        vec![lower_layer, higher_layer],        ConfigRequirements::default(),        ConfigRequirementsToml::default(),    )    .expect("layer stack should be valid");    let constraints = network_constraints_from_trusted_layers(&layers)        .expect("final built-in trusted selection should load");    assert_eq!(constraints.allowed_domains, None);    assert_eq!(constraints.denied_domains, None);}#[test]fn trusted_constraints_normalize_profile_network_domains_before_merging_layers() {    let lower_layer = ConfigLayerEntry::new(        ConfigLayerSource::System {            file: AbsolutePathBuf::try_from(std::path::PathBuf::from("/tmp/system.toml"))                .expect("system config path should be absolute"),        },        toml::toml! {            default_permissions = "dev"            [permissions.dev.network.domains]            "example.com" = "deny"        }        .into(),    );    let higher_layer = ConfigLayerEntry::new(        ConfigLayerSource::LegacyManagedConfigTomlFromFile {            file: AbsolutePathBuf::try_from(std::path::PathBuf::from("/tmp/managed.toml"))                .expect("managed config path should be absolute"),        },        toml::toml! {            [permissions.dev.network.domains]            "EXAMPLE.COM" = "allow"        }        .into(),    );    let layers = ConfigLayerStack::new(        vec![lower_layer, higher_layer],        ConfigRequirements::default(),        ConfigRequirementsToml::default(),    )    .expect("layer stack should be valid");    let constraints = network_constraints_from_trusted_layers(&layers)        .expect("trusted network domain layer precedence should load");    assert_eq!(        constraints.allowed_domains,        Some(vec!["example.com".to_string()])    );    assert_eq!(constraints.denied_domains, None);}#[test]fn apply_network_constraints_skips_empty_domain_sides() {    let config: toml::Value = toml::from_str(        r#"default_permissions = "dev"[permissions.dev.network][permissions.dev.network.domains]"managed.example.com" = "allow""#,    )    .expect("permissions profile should parse");    let network = selected_network_from_tables(        network_tables_from_toml(&config).expect("permissions profile should deserialize"),    )    .expect("permissions profile should select a network table")    .expect("network table should be present");    let mut constraints = NetworkProxyConstraints::default();    apply_network_constraints(network, &mut constraints);    assert_eq!(        constraints.allowed_domains,        Some(vec!["managed.example.com".to_string()])    );    assert_eq!(constraints.denied_domains, None);}#[test]fn apply_network_constraints_overlay_domain_entries() {    let lower_network: toml::Value = toml::from_str(        r#"default_permissions = "dev"[permissions.dev.network][permissions.dev.network.domains]"blocked.example.com" = "deny""#,    )    .expect("lower layer should parse");    let higher_network: toml::Value = toml::from_str(        r#"default_permissions = "dev"[permissions.dev.network][permissions.dev.network.domains]"api.example.com" = "allow""#,    )    .expect("higher layer should parse");    let lower_network = selected_network_from_tables(        network_tables_from_toml(&lower_network).expect("lower layer should deserialize"),    )    .expect("lower layer should select a network table")    .expect("lower network table should be present");    let higher_network = selected_network_from_tables(        network_tables_from_toml(&higher_network).expect("higher layer should deserialize"),    )    .expect("higher layer should select a network table")    .expect("higher network table should be present");    let mut constraints = NetworkProxyConstraints::default();    apply_network_constraints(lower_network, &mut constraints);    apply_network_constraints(higher_network, &mut constraints);    assert_eq!(        constraints.allowed_domains,        Some(vec!["api.example.com".to_string()])    );    assert_eq!(        constraints.denied_domains,        Some(vec!["blocked.example.com".to_string()])    );}