Files
codex/codex-rs/network-proxy/src/main.rs
T
viyatb-oai 457441bca9 Add constraint-aware standalone network proxy configuration (#57180)
## What changed

- Export `StaticNetworkProxyConfig` to parse and serialize network policy with constraints, and validate them before building proxy state.
- Add `--strict` and `from_slice_strict` for managed callers, requiring explicit `constraints` and rejecting unknown fields at every typed level. Default parsing permits top-level `metadata` and omitted constraints while still rejecting unknown policy fields.
- Match allowed Unix socket paths case-sensitively when validating constraints.
- Bound configuration file reads to the size limit plus one byte.

## Testing

Add coverage for constraint round trips, weakened policy rejection, exact socket path matching, parsing modes, and CLI flag selection. Exercise automatic MITM enablement through both parsers.

GitOrigin-RevId: c45d094624240f23dd4659128e72a4208d5d5f1c
2026-10-26 18:22:40 +00:00

104 lines
2.9 KiB
Rust

#![deny(clippy::print_stdout, clippy::print_stderr)]
use anyhow::Context as _;
use anyhow::Result;
use anyhow::ensure;
use clap::Parser;
use codex_network_proxy::ConfigReloader;
use codex_network_proxy::ConfigReloaderFuture;
use codex_network_proxy::ConfigState;
use codex_network_proxy::NetworkProxy;
use codex_network_proxy::NetworkProxyState;
use codex_network_proxy::Platform;
use codex_network_proxy::StaticNetworkProxyConfig;
use std::io::Read;
use std::path::PathBuf;
use std::sync::Arc;
use tracing_subscriber::EnvFilter;
const MAX_CONFIG_BYTES: usize = 1024 * 1024;
#[derive(Debug, Parser)]
#[command(name = "codex-network-proxy", about = "Codex network policy proxy")]
struct Args {
/// Standalone JSON configuration containing a `network` object.
#[arg(long, value_name = "PATH")]
config: PathBuf,
/// Reject unknown config fields and require explicit managed constraints.
#[arg(long)]
strict: bool,
}
impl Args {
fn parse_config(&self, bytes: &[u8]) -> Result<StaticNetworkProxyConfig> {
if self.strict {
StaticNetworkProxyConfig::from_slice_strict(bytes)
} else {
StaticNetworkProxyConfig::from_slice(bytes)
}
}
}
struct StaticConfigReloader {
state: ConfigState,
}
impl ConfigReloader for StaticConfigReloader {
fn source_label(&self) -> String {
"standalone network proxy config".to_string()
}
fn maybe_reload(&self) -> ConfigReloaderFuture<'_, Option<ConfigState>> {
Box::pin(async { Ok(None) })
}
fn reload_now(&self) -> ConfigReloaderFuture<'_, ConfigState> {
Box::pin(async { Ok(self.state.clone()) })
}
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.init();
let args = Args::parse();
let mut bytes = Vec::new();
std::fs::File::open(&args.config)
.with_context(|| {
format!(
"failed to read network proxy config: {}",
args.config.display()
)
})?
.take((MAX_CONFIG_BYTES + 1) as u64)
.read_to_end(&mut bytes)?;
ensure!(
bytes.len() <= MAX_CONFIG_BYTES,
"network proxy config exceeds {MAX_CONFIG_BYTES} bytes"
);
let config = args
.parse_config(&bytes)
.with_context(|| format!("invalid network proxy config: {}", args.config.display()))?;
let config_state = config
.build_state(Platform::native())
.context("failed to initialize network proxy policy")?;
let reloader = Arc::new(StaticConfigReloader {
state: config_state.clone(),
});
let state = Arc::new(NetworkProxyState::with_reloader(config_state, reloader));
NetworkProxy::builder()
.state(state)
.managed_by_codex(/*managed_by_codex*/ false)
.build()
.await?
.run()
.await?
.wait()
.await
}
#[cfg(test)]
#[path = "main_tests.rs"]
mod tests;