Add MITM hook config model

This commit is contained in:
Eva Wong
2026-05-15 05:20:17 -07:00
committed by Winston Howes
parent c03cea4ca2
commit 4334b19c23
8 changed files with 1401 additions and 3 deletions
+81 -1
View File
@@ -69,6 +69,11 @@ use codex_model_provider_info::LMSTUDIO_OSS_PROVIDER_ID;
use codex_model_provider_info::OLLAMA_OSS_PROVIDER_ID;
use codex_model_provider_info::WireApi;
use codex_models_manager::bundled_models_response;
use codex_network_proxy::InjectedHeaderConfig;
use codex_network_proxy::MitmHookActionsConfig;
use codex_network_proxy::MitmHookConfig;
use codex_network_proxy::MitmHookMatchConfig;
use codex_network_proxy::NetworkMode;
use codex_protocol::config_types::ServiceTier;
use codex_protocol::models::ActivePermissionProfile;
use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS;
@@ -750,9 +755,29 @@ enabled = true
proxy_url = "http://127.0.0.1:43128"
enable_socks5 = false
allow_upstream_proxy = false
mode = "full"
mitm = true
[permissions.workspace.network.domains]
"openai.com" = "allow"
[[permissions.workspace.network.mitm_hooks]]
host = "api.github.com"
[permissions.workspace.network.mitm_hooks.match]
methods = ["POST", "PUT"]
path_prefixes = ["/repos/openai/"]
[permissions.workspace.network.mitm_hooks.match.headers]
"x-github-api-version" = ["2022-11-28"]
[permissions.workspace.network.mitm_hooks.actions]
strip_request_headers = ["authorization"]
[[permissions.workspace.network.mitm_hooks.actions.inject_request_headers]]
name = "authorization"
secret_env_var = "CODEX_GITHUB_TOKEN"
prefix = "Bearer "
"#;
let cfg: ConfigToml =
toml::from_str(toml).expect("TOML deserialization should succeed for permissions profiles");
@@ -795,7 +820,7 @@ allow_upstream_proxy = false
allow_upstream_proxy: Some(false),
dangerously_allow_non_loopback_proxy: None,
dangerously_allow_all_unix_sockets: None,
mode: None,
mode: Some(NetworkMode::Full),
domains: Some(NetworkDomainPermissionsToml {
entries: BTreeMap::from([(
"openai.com".to_string(),
@@ -804,6 +829,29 @@ allow_upstream_proxy = false
}),
unix_sockets: None,
allow_local_binding: None,
mitm: Some(true),
mitm_hooks: Some(vec![MitmHookConfig {
host: "api.github.com".to_string(),
matcher: MitmHookMatchConfig {
methods: vec!["POST".to_string(), "PUT".to_string()],
path_prefixes: vec!["/repos/openai/".to_string()],
query: BTreeMap::new(),
headers: BTreeMap::from([(
"x-github-api-version".to_string(),
vec!["2022-11-28".to_string()],
)]),
body: None,
},
actions: MitmHookActionsConfig {
strip_request_headers: vec!["authorization".to_string()],
inject_request_headers: vec![InjectedHeaderConfig {
name: "authorization".to_string(),
secret_env_var: Some("CODEX_GITHUB_TOKEN".to_string()),
secret_file: None,
prefix: Some("Bearer ".to_string()),
}],
},
}]),
}),
},
)]),
@@ -811,6 +859,38 @@ allow_upstream_proxy = false
);
}
#[test]
fn permissions_profile_network_to_proxy_config_preserves_mitm_hooks() {
let network = NetworkToml {
mode: Some(NetworkMode::Full),
mitm: Some(true),
mitm_hooks: Some(vec![MitmHookConfig {
host: "api.github.com".to_string(),
matcher: MitmHookMatchConfig {
methods: vec!["POST".to_string()],
path_prefixes: vec!["/repos/openai/".to_string()],
..MitmHookMatchConfig::default()
},
actions: MitmHookActionsConfig {
strip_request_headers: vec!["authorization".to_string()],
inject_request_headers: vec![InjectedHeaderConfig {
name: "authorization".to_string(),
secret_env_var: Some("CODEX_GITHUB_TOKEN".to_string()),
secret_file: None,
prefix: Some("Bearer ".to_string()),
}],
},
}]),
..NetworkToml::default()
};
let config = network.to_network_proxy_config();
assert_eq!(config.network.mode, NetworkMode::Full);
assert!(config.network.mitm);
assert_eq!(config.network.mitm_hooks, network.mitm_hooks.unwrap());
}
#[tokio::test]
async fn permissions_profiles_proxy_policy_does_not_start_managed_network_proxy_without_feature()
-> std::io::Result<()> {
@@ -104,6 +104,75 @@ default_permissions = "workspace"
assert_eq!(config.network.denied_domains(), None);
}
#[test]
fn higher_precedence_profile_network_overrides_mitm_hooks() {
let lower_network: toml::Value = toml::from_str(
r#"
default_permissions = "workspace"
[permissions.workspace.network]
mode = "limited"
mitm = false
[permissions.workspace.network.domains]
"lower.example.com" = "allow"
[[permissions.workspace.network.mitm_hooks]]
host = "lower.example.com"
[permissions.workspace.network.mitm_hooks.match]
methods = ["POST"]
path_prefixes = ["/repos/openai/"]
"#,
)
.expect("lower layer should parse");
let higher_network: toml::Value = toml::from_str(
r#"
default_permissions = "workspace"
[permissions.workspace.network]
mode = "full"
mitm = true
[permissions.workspace.network.domains]
"higher.example.com" = "allow"
[[permissions.workspace.network.mitm_hooks]]
host = "api.github.com"
[permissions.workspace.network.mitm_hooks.match]
methods = ["PUT"]
path_prefixes = ["/repos/openai/"]
"#,
)
.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.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!["PUT"]);
}
#[test]
fn execpolicy_network_rules_overlay_network_lists() {
let mut config = NetworkProxyConfig::default();