mirror of
https://github.com/openai/codex.git
synced 2026-05-04 05:11:37 +03:00
Allow global network allowlist wildcard (#15549)
## Problem Today `codex-network-proxy` rejects a global `*` in `network.allowed_domains`, so there is no static way to configure a denylist-only posture for public hosts. Users have to enumerate broad allowlist patterns instead. ## Approach - Make global wildcard acceptance field-specific: `allowed_domains` can use `*`, while `denied_domains` still rejects a global wildcard. - Keep the existing evaluation order, so explicit denies still win first and local/private protections still apply unless separately enabled. - Add coverage for the denylist-only behavior and update the README to document it. ## Validation - `just fmt` - `cargo test -p codex-network-proxy` (full run had one unrelated flaky telemetry test: `network_policy::tests::emit_block_decision_audit_event_emits_non_domain_event`; reran in isolation and it passed) - `cargo test -p codex-network-proxy network_policy::tests::emit_block_decision_audit_event_emits_non_domain_event -- --exact --nocapture` - `just fix -p codex-network-proxy` - `just argument-comment-lint`
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
use crate::config::NetworkMode;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use anyhow::bail;
|
||||
use anyhow::ensure;
|
||||
use globset::GlobBuilder;
|
||||
use globset::GlobSet;
|
||||
@@ -151,19 +152,38 @@ pub(crate) fn is_global_wildcard_domain_pattern(pattern: &str) -> bool {
|
||||
.any(|candidate| candidate == "*")
|
||||
}
|
||||
|
||||
pub(crate) fn compile_globset(patterns: &[String]) -> Result<GlobSet> {
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum GlobalWildcard {
|
||||
Allow,
|
||||
Reject,
|
||||
}
|
||||
|
||||
pub(crate) fn compile_allowlist_globset(patterns: &[String]) -> Result<GlobSet> {
|
||||
compile_globset_with_policy(patterns, GlobalWildcard::Allow)
|
||||
}
|
||||
|
||||
pub(crate) fn compile_denylist_globset(patterns: &[String]) -> Result<GlobSet> {
|
||||
compile_globset_with_policy(patterns, GlobalWildcard::Reject)
|
||||
}
|
||||
|
||||
fn compile_globset_with_policy(
|
||||
patterns: &[String],
|
||||
global_wildcard: GlobalWildcard,
|
||||
) -> Result<GlobSet> {
|
||||
let mut builder = GlobSetBuilder::new();
|
||||
let mut seen = HashSet::new();
|
||||
for pattern in patterns {
|
||||
ensure!(
|
||||
!is_global_wildcard_domain_pattern(pattern),
|
||||
"unsupported global wildcard domain pattern \"*\"; use exact hosts or scoped wildcards like *.example.com or **.example.com"
|
||||
);
|
||||
if global_wildcard == GlobalWildcard::Reject && is_global_wildcard_domain_pattern(pattern) {
|
||||
bail!(
|
||||
"unsupported global wildcard domain pattern \"*\"; use exact hosts or scoped wildcards like *.example.com or **.example.com"
|
||||
);
|
||||
}
|
||||
let pattern = normalize_pattern(pattern);
|
||||
// Supported domain patterns:
|
||||
// - "example.com": match the exact host
|
||||
// - "*.example.com": match any subdomain (not the apex)
|
||||
// - "**.example.com": match the apex and any subdomain
|
||||
// - "*": match every host when explicitly enabled for allowlist compilation
|
||||
for candidate in expand_domain_pattern(&pattern) {
|
||||
if !seen.insert(candidate.clone()) {
|
||||
continue;
|
||||
@@ -333,7 +353,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn compile_globset_normalizes_trailing_dots() {
|
||||
let set = compile_globset(&["Example.COM.".to_string()]).unwrap();
|
||||
let set = compile_denylist_globset(&["Example.COM.".to_string()]).unwrap();
|
||||
|
||||
assert_eq!(true, set.is_match("example.com"));
|
||||
assert_eq!(false, set.is_match("api.example.com"));
|
||||
@@ -341,7 +361,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn compile_globset_normalizes_wildcards() {
|
||||
let set = compile_globset(&["*.Example.COM.".to_string()]).unwrap();
|
||||
let set = compile_denylist_globset(&["*.Example.COM.".to_string()]).unwrap();
|
||||
|
||||
assert_eq!(true, set.is_match("api.example.com"));
|
||||
assert_eq!(false, set.is_match("example.com"));
|
||||
@@ -349,7 +369,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn compile_globset_normalizes_apex_and_subdomains() {
|
||||
let set = compile_globset(&["**.Example.COM.".to_string()]).unwrap();
|
||||
let set = compile_denylist_globset(&["**.Example.COM.".to_string()]).unwrap();
|
||||
|
||||
assert_eq!(true, set.is_match("example.com"));
|
||||
assert_eq!(true, set.is_match("api.example.com"));
|
||||
@@ -357,7 +377,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn compile_globset_normalizes_bracketed_ipv6_literals() {
|
||||
let set = compile_globset(&["[::1]".to_string()]).unwrap();
|
||||
let set = compile_denylist_globset(&["[::1]".to_string()]).unwrap();
|
||||
|
||||
assert_eq!(true, set.is_match("::1"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user