fix: reject global wildcard network proxy domains (#13789)

## Summary
- reject the global `*` domain pattern in proxy allow/deny lists and
managed constraints introduced for testing earlier
- keep exact hosts plus scoped wildcards like `*.example.com` and
`**.example.com`
- update docs and regression tests for the new invalid-config behavior
This commit is contained in:
viyatb-oai
2026-03-06 13:06:24 -08:00
committed by GitHub
parent 7a5aff4972
commit 9a4787c240
5 changed files with 168 additions and 45 deletions

View File

@@ -821,6 +821,7 @@ mod tests {
use crate::config::NetworkProxySettings;
use crate::policy::compile_globset;
use crate::state::NetworkProxyConstraints;
use crate::state::build_config_state;
use crate::state::validate_policy_against_constraints;
use pretty_assertions::assert_eq;
@@ -1014,34 +1015,6 @@ mod tests {
);
}
#[tokio::test]
async fn host_blocked_rejects_loopback_when_allowlist_is_wildcard() {
let state = network_proxy_state_for_policy(NetworkProxySettings {
allowed_domains: vec!["*".to_string()],
allow_local_binding: false,
..NetworkProxySettings::default()
});
assert_eq!(
state.host_blocked("127.0.0.1", 80).await.unwrap(),
HostBlockDecision::Blocked(HostBlockReason::NotAllowedLocal)
);
}
#[tokio::test]
async fn host_blocked_rejects_private_ip_literal_when_allowlist_is_wildcard() {
let state = network_proxy_state_for_policy(NetworkProxySettings {
allowed_domains: vec!["*".to_string()],
allow_local_binding: false,
..NetworkProxySettings::default()
});
assert_eq!(
state.host_blocked("10.0.0.1", 80).await.unwrap(),
HostBlockDecision::Blocked(HostBlockReason::NotAllowedLocal)
);
}
#[tokio::test]
async fn host_blocked_allows_loopback_when_explicitly_allowlisted_and_local_binding_disabled() {
let state = network_proxy_state_for_policy(NetworkProxySettings {
@@ -1198,6 +1171,62 @@ mod tests {
assert!(validate_policy_against_constraints(&config, &constraints).is_err());
}
#[test]
fn validate_policy_against_constraints_rejects_global_wildcard_in_managed_allowlist() {
let constraints = NetworkProxyConstraints {
allowed_domains: Some(vec!["*".to_string()]),
..NetworkProxyConstraints::default()
};
let config = NetworkProxyConfig {
network: NetworkProxySettings {
enabled: true,
allowed_domains: vec!["api.example.com".to_string()],
..NetworkProxySettings::default()
},
};
assert!(validate_policy_against_constraints(&config, &constraints).is_err());
}
#[test]
fn validate_policy_against_constraints_rejects_bracketed_global_wildcard_in_managed_allowlist()
{
let constraints = NetworkProxyConstraints {
allowed_domains: Some(vec!["[*]".to_string()]),
..NetworkProxyConstraints::default()
};
let config = NetworkProxyConfig {
network: NetworkProxySettings {
enabled: true,
allowed_domains: vec!["api.example.com".to_string()],
..NetworkProxySettings::default()
},
};
assert!(validate_policy_against_constraints(&config, &constraints).is_err());
}
#[test]
fn validate_policy_against_constraints_rejects_double_wildcard_bracketed_global_wildcard_in_managed_allowlist()
{
let constraints = NetworkProxyConstraints {
allowed_domains: Some(vec!["**.[*]".to_string()]),
..NetworkProxyConstraints::default()
};
let config = NetworkProxyConfig {
network: NetworkProxySettings {
enabled: true,
allowed_domains: vec!["api.example.com".to_string()],
..NetworkProxySettings::default()
},
};
assert!(validate_policy_against_constraints(&config, &constraints).is_err());
}
#[test]
fn validate_policy_against_constraints_requires_managed_denied_domains_entries() {
let constraints = NetworkProxyConstraints {
@@ -1349,11 +1378,21 @@ mod tests {
}
#[test]
fn compile_globset_matches_all_with_star() {
fn compile_globset_rejects_global_wildcard() {
let patterns = vec!["*".to_string()];
let set = compile_globset(&patterns).unwrap();
assert!(set.is_match("openai.com"));
assert!(set.is_match("api.openai.com"));
assert!(compile_globset(&patterns).is_err());
}
#[test]
fn compile_globset_rejects_bracketed_global_wildcard() {
let patterns = vec!["[*]".to_string()];
assert!(compile_globset(&patterns).is_err());
}
#[test]
fn compile_globset_rejects_double_wildcard_bracketed_global_wildcard() {
let patterns = vec!["**.[*]".to_string()];
assert!(compile_globset(&patterns).is_err());
}
#[test]
@@ -1371,6 +1410,60 @@ mod tests {
assert!(compile_globset(&patterns).is_err());
}
#[test]
fn build_config_state_rejects_global_wildcard_allowed_domains() {
let config = NetworkProxyConfig {
network: NetworkProxySettings {
enabled: true,
allowed_domains: vec!["*".to_string()],
..NetworkProxySettings::default()
},
};
assert!(build_config_state(config, NetworkProxyConstraints::default()).is_err());
}
#[test]
fn build_config_state_rejects_bracketed_global_wildcard_allowed_domains() {
let config = NetworkProxyConfig {
network: NetworkProxySettings {
enabled: true,
allowed_domains: vec!["[*]".to_string()],
..NetworkProxySettings::default()
},
};
assert!(build_config_state(config, NetworkProxyConstraints::default()).is_err());
}
#[test]
fn build_config_state_rejects_global_wildcard_denied_domains() {
let config = NetworkProxyConfig {
network: NetworkProxySettings {
enabled: true,
allowed_domains: vec!["example.com".to_string()],
denied_domains: vec!["*".to_string()],
..NetworkProxySettings::default()
},
};
assert!(build_config_state(config, NetworkProxyConstraints::default()).is_err());
}
#[test]
fn build_config_state_rejects_bracketed_global_wildcard_denied_domains() {
let config = NetworkProxyConfig {
network: NetworkProxySettings {
enabled: true,
allowed_domains: vec!["example.com".to_string()],
denied_domains: vec!["[*]".to_string()],
..NetworkProxySettings::default()
},
};
assert!(build_config_state(config, NetworkProxyConstraints::default()).is_err());
}
#[cfg(target_os = "macos")]
#[tokio::test]
async fn unix_socket_allowlist_is_respected_on_macos() {