mirror of
https://github.com/openai/codex.git
synced 2026-09-06 15:29:32 +00:00
Preserve trusted MITM hook constraints
This commit is contained in:
@@ -122,6 +122,7 @@ fn network_constraints_from_trusted_layers(
|
||||
layers: &ConfigLayerStack,
|
||||
) -> Result<NetworkProxyConstraints> {
|
||||
let mut constraints = NetworkProxyConstraints::default();
|
||||
let mut trusted_network = NetworkConfigAccumulator::default();
|
||||
for layer in layers.get_layers(
|
||||
ConfigLayerStackOrdering::LowestPrecedenceFirst,
|
||||
/*include_disabled*/ false,
|
||||
@@ -132,9 +133,13 @@ fn network_constraints_from_trusted_layers(
|
||||
|
||||
let parsed = network_tables_from_toml(&layer.config)?;
|
||||
if let Some(network) = selected_network_from_tables(parsed)? {
|
||||
apply_network_constraints(network, &mut constraints);
|
||||
apply_network_constraints(network.clone(), &mut constraints);
|
||||
trusted_network.apply_network(network);
|
||||
}
|
||||
}
|
||||
let trusted_mitm_hooks = trusted_network.finish()?.network.mitm_hooks;
|
||||
constraints.required_mitm_hook_prefix =
|
||||
(!trusted_mitm_hooks.is_empty()).then_some(trusted_mitm_hooks);
|
||||
Ok(constraints)
|
||||
}
|
||||
|
||||
|
||||
@@ -174,6 +174,72 @@ strip_request_headers = ["x-api-key"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted_named_mitm_actions_become_required_hook_constraints() {
|
||||
let lower_network: toml::Value = toml::from_str(
|
||||
r#"
|
||||
default_permissions = "workspace"
|
||||
|
||||
[permissions.workspace.network]
|
||||
mode = "full"
|
||||
|
||||
[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.mitm.actions.strip_auth]
|
||||
strip_request_headers = ["x-api-key"]
|
||||
"#,
|
||||
)
|
||||
.expect("higher layer should parse");
|
||||
|
||||
let mut trusted_network = NetworkConfigAccumulator::default();
|
||||
trusted_network
|
||||
.apply_network_tables(
|
||||
network_tables_from_toml(&lower_network).expect("lower layer should deserialize"),
|
||||
)
|
||||
.expect("lower layer should apply");
|
||||
trusted_network
|
||||
.apply_network_tables(
|
||||
network_tables_from_toml(&higher_network).expect("higher layer should deserialize"),
|
||||
)
|
||||
.expect("higher layer should apply");
|
||||
|
||||
let constraints = NetworkProxyConstraints {
|
||||
required_mitm_hook_prefix: Some(
|
||||
trusted_network
|
||||
.finish()
|
||||
.expect("trusted network should build")
|
||||
.network
|
||||
.mitm_hooks,
|
||||
),
|
||||
..NetworkProxyConstraints::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
constraints
|
||||
.required_mitm_hook_prefix
|
||||
.expect("trusted hooks should be constrained")[0]
|
||||
.actions
|
||||
.strip_request_headers,
|
||||
vec!["x-api-key"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execpolicy_network_rules_overlay_network_lists() {
|
||||
let mut config = NetworkProxyConfig::default();
|
||||
|
||||
@@ -1529,6 +1529,61 @@ mod tests {
|
||||
assert!(validate_policy_against_constraints(&config, &constraints).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_policy_against_constraints_preserves_managed_mitm_hooks() {
|
||||
let managed_hook = crate::mitm_hook::MitmHookConfig {
|
||||
host: "api.github.com".to_string(),
|
||||
matcher: crate::mitm_hook::MitmHookMatchConfig {
|
||||
methods: vec!["POST".to_string()],
|
||||
path_prefixes: vec!["/repos/openai/".to_string()],
|
||||
..crate::mitm_hook::MitmHookMatchConfig::default()
|
||||
},
|
||||
actions: crate::mitm_hook::MitmHookActionsConfig {
|
||||
strip_request_headers: vec!["authorization".to_string()],
|
||||
..crate::mitm_hook::MitmHookActionsConfig::default()
|
||||
},
|
||||
};
|
||||
let extra_hook = crate::mitm_hook::MitmHookConfig {
|
||||
host: "api.example.com".to_string(),
|
||||
matcher: crate::mitm_hook::MitmHookMatchConfig {
|
||||
methods: vec!["GET".to_string()],
|
||||
path_prefixes: vec!["/".to_string()],
|
||||
..crate::mitm_hook::MitmHookMatchConfig::default()
|
||||
},
|
||||
..crate::mitm_hook::MitmHookConfig::default()
|
||||
};
|
||||
let constraints = NetworkProxyConstraints {
|
||||
required_mitm_hook_prefix: Some(vec![managed_hook.clone()]),
|
||||
..NetworkProxyConstraints::default()
|
||||
};
|
||||
|
||||
let config = NetworkProxyConfig {
|
||||
network: NetworkProxySettings {
|
||||
enabled: true,
|
||||
mitm: true,
|
||||
mitm_hooks: vec![managed_hook.clone(), extra_hook],
|
||||
..NetworkProxySettings::default()
|
||||
},
|
||||
};
|
||||
assert!(validate_policy_against_constraints(&config, &constraints).is_ok());
|
||||
|
||||
let config = NetworkProxyConfig {
|
||||
network: NetworkProxySettings {
|
||||
enabled: true,
|
||||
mitm: true,
|
||||
mitm_hooks: vec![crate::mitm_hook::MitmHookConfig {
|
||||
actions: crate::mitm_hook::MitmHookActionsConfig {
|
||||
strip_request_headers: vec!["x-api-key".to_string()],
|
||||
..crate::mitm_hook::MitmHookActionsConfig::default()
|
||||
},
|
||||
..managed_hook
|
||||
}],
|
||||
..NetworkProxySettings::default()
|
||||
},
|
||||
};
|
||||
assert!(validate_policy_against_constraints(&config, &constraints).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_policy_against_constraints_allows_narrowing_wildcard_allowlist() {
|
||||
let constraints = NetworkProxyConstraints {
|
||||
|
||||
@@ -36,6 +36,7 @@ pub struct NetworkProxyConstraints {
|
||||
pub denylist_expansion_enabled: Option<bool>,
|
||||
pub allow_unix_sockets: Option<Vec<String>>,
|
||||
pub allow_local_binding: Option<bool>,
|
||||
pub required_mitm_hook_prefix: Option<Vec<MitmHookConfig>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
@@ -126,6 +127,18 @@ pub fn validate_policy_against_constraints(
|
||||
let config_allow_unix_sockets = config.network.allow_unix_sockets();
|
||||
validate_mitm_hook_config(config).map_err(invalid_mitm_hook_configuration)?;
|
||||
validate_non_global_wildcard_domain_patterns("network.denied_domains", &config_denied_domains)?;
|
||||
if let Some(required_mitm_hook_prefix) = constraints.required_mitm_hook_prefix.as_ref()
|
||||
&& !config
|
||||
.network
|
||||
.mitm_hooks
|
||||
.starts_with(required_mitm_hook_prefix)
|
||||
{
|
||||
return Err(invalid_value(
|
||||
"network.mitm_hooks",
|
||||
"managed MITM hooks were replaced or reordered",
|
||||
"managed MITM hooks preserved before lower-trust hooks",
|
||||
));
|
||||
}
|
||||
if let Some(max_enabled) = constraints.enabled {
|
||||
validate(enabled, move |candidate| {
|
||||
if *candidate && !max_enabled {
|
||||
|
||||
Reference in New Issue
Block a user