Scope network approvals by environment (#28899)

Stacked on #28766.

## Why

Network approvals are environment-scoped: allowing a host in one
execution environment should not allow the same host in another
environment.

#28766 adds the inert IDs and constructor plumbing. This PR applies the
behavior on top.

## What changed

- Route managed network traffic through per-environment HTTP and SOCKS
proxy listeners.
- Stamp HTTP, HTTPS CONNECT, SOCKS TCP, and SOCKS UDP policy requests
with the source environment at the proxy boundary.
- Carry the selected execution environment through shell, unified exec,
zsh-fork, and sandbox transform paths.
- Include the environment in pending, approved-for-session, and
denied-for-session network approval cache keys.
- Include the environment in approval IDs and approval prompts.
- Preserve legacy fallback for unattributed requests, but deny when
active-call attribution is ambiguous.
- Fail closed if an environment-specific proxy endpoint cannot be
prepared.

## Validation

- just fmt
- CI will run tests and clippy
This commit is contained in:
jif
2026-06-19 12:49:45 +01:00
committed by GitHub
parent dac588f413
commit bd5d31b250
20 changed files with 1026 additions and 109 deletions

View File

@@ -87,6 +87,7 @@ pub async fn run_http_proxy(
state: Arc<NetworkProxyState>,
addr: SocketAddr,
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
environment_id: Option<String>,
) -> Result<()> {
let listener = TcpListener::build()
.bind(addr)
@@ -99,23 +100,25 @@ pub async fn run_http_proxy(
.map_err(anyhow::Error::from)
.with_context(|| format!("bind HTTP proxy: {addr}"))?;
run_http_proxy_with_listener(state, listener, policy_decider).await
run_http_proxy_with_listener(state, listener, policy_decider, environment_id).await
}
pub async fn run_http_proxy_with_std_listener(
state: Arc<NetworkProxyState>,
listener: StdTcpListener,
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
environment_id: Option<String>,
) -> Result<()> {
let listener =
TcpListener::try_from(listener).context("convert std listener to HTTP proxy listener")?;
run_http_proxy_with_listener(state, listener, policy_decider).await
run_http_proxy_with_listener(state, listener, policy_decider, environment_id).await
}
async fn run_http_proxy_with_listener(
state: Arc<NetworkProxyState>,
listener: TcpListener,
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
environment_id: Option<String>,
) -> Result<()> {
ensure_rustls_crypto_provider();
@@ -133,7 +136,10 @@ async fn run_http_proxy_with_listener(
MethodMatcher::CONNECT,
service_fn({
let policy_decider = policy_decider.clone();
move |req| http_connect_accept(policy_decider.clone(), req)
let environment_id = environment_id.clone();
move |req| {
http_connect_accept(policy_decider.clone(), environment_id.clone(), req)
}
}),
service_fn(http_connect_proxy),
),
@@ -141,7 +147,8 @@ async fn run_http_proxy_with_listener(
)
.into_layer(service_fn({
let policy_decider = policy_decider.clone();
move |req| http_plain_proxy(policy_decider.clone(), req)
let environment_id = environment_id.clone();
move |req| http_plain_proxy(policy_decider.clone(), environment_id.clone(), req)
})),
);
@@ -155,6 +162,7 @@ async fn run_http_proxy_with_listener(
async fn http_connect_accept(
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
environment_id: Option<String>,
mut req: Request,
) -> Result<(Response, Request), Response> {
let app_state = req
@@ -200,7 +208,7 @@ async fn http_connect_accept(
protocol: NetworkProtocol::HttpsConnect,
host: host.clone(),
port: authority.port,
environment_id: None,
environment_id,
client_addr: client.clone(),
method: Some("CONNECT".to_string()),
command: None,
@@ -479,6 +487,7 @@ async fn forward_connect_tunnel(
async fn http_plain_proxy(
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
environment_id: Option<String>,
mut req: Request,
) -> Result<Response, Infallible> {
let app_state = match req.extensions().get::<Arc<NetworkProxyState>>().cloned() {
@@ -684,7 +693,7 @@ async fn http_plain_proxy(
protocol: NetworkProtocol::Http,
host: host.clone(),
port,
environment_id: None,
environment_id,
client_addr: client.clone(),
method: Some(req.method().as_str().to_string()),
command: None,
@@ -1052,6 +1061,7 @@ mod tests {
use std::net::Ipv4Addr;
use std::net::TcpListener as StdTcpListener;
use std::sync::Arc;
use std::sync::Mutex;
use tokio::io::AsyncReadExt;
use tokio::io::AsyncWriteExt;
use tokio::net::TcpListener as TokioTcpListener;
@@ -1076,9 +1086,11 @@ mod tests {
.unwrap();
req.extensions_mut().insert(state);
let response = http_connect_accept(/*policy_decider*/ None, req)
.await
.unwrap_err();
let response = http_connect_accept(
/*policy_decider*/ None, /*environment_id*/ None, req,
)
.await
.unwrap_err();
assert_eq!(response.status(), StatusCode::FORBIDDEN);
assert_eq!(
response.headers().get("x-proxy-error").unwrap(),
@@ -1106,12 +1118,53 @@ mod tests {
.unwrap();
req.extensions_mut().insert(state);
let (response, _request) = http_connect_accept(/*policy_decider*/ None, req)
.await
.unwrap();
let (response, _request) = http_connect_accept(
/*policy_decider*/ None, /*environment_id*/ None, req,
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
#[tokio::test]
async fn http_connect_accept_passes_environment_id_to_decider() {
let state = Arc::new(network_proxy_state_for_policy(
NetworkProxySettings::default(),
));
let seen_environment_id = Arc::new(Mutex::new(None));
let decider: Arc<dyn NetworkPolicyDecider> = Arc::new({
let seen_environment_id = seen_environment_id.clone();
move |request: NetworkPolicyRequest| {
*seen_environment_id
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = request.environment_id;
async { NetworkDecision::Allow }
}
});
let mut req = Request::builder()
.method(Method::CONNECT)
.uri("https://example.com:443")
.header("host", "example.com:443")
.body(Body::empty())
.unwrap();
req.extensions_mut().insert(state);
let (response, _request) =
http_connect_accept(Some(decider), Some("remote".to_string()), req)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
assert_eq!(
seen_environment_id
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.as_deref(),
Some("remote")
);
}
#[tokio::test]
async fn http_connect_accept_blocks_hooked_host_in_full_mode_without_mitm_state() {
let mut policy = NetworkProxySettings {
@@ -1138,9 +1191,11 @@ mod tests {
.unwrap();
req.extensions_mut().insert(state);
let response = http_connect_accept(/*policy_decider*/ None, req)
.await
.unwrap_err();
let response = http_connect_accept(
/*policy_decider*/ None, /*environment_id*/ None, req,
)
.await
.unwrap_err();
assert_eq!(response.status(), StatusCode::FORBIDDEN);
assert_eq!(
response.headers().get("x-proxy-error").unwrap(),
@@ -1177,7 +1232,7 @@ mod tests {
.local_addr()
.expect("proxy listener should expose local addr");
let proxy_task = tokio::spawn(run_http_proxy_with_std_listener(
state, listener, /*policy_decider*/ None,
state, listener, /*policy_decider*/ None, /*environment_id*/ None,
));
let mut stream = tokio::net::TcpStream::connect(proxy_addr)
@@ -1228,9 +1283,11 @@ mod tests {
.expect("request should build");
req.extensions_mut().insert(state);
let response = http_plain_proxy(/*policy_decider*/ None, req)
.await
.unwrap();
let response = http_plain_proxy(
/*policy_decider*/ None, /*environment_id*/ None, req,
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::FORBIDDEN);
assert_eq!(
@@ -1253,9 +1310,11 @@ mod tests {
.expect("request should build");
req.extensions_mut().insert(state);
let response = http_plain_proxy(/*policy_decider*/ None, req)
.await
.unwrap();
let response = http_plain_proxy(
/*policy_decider*/ None, /*environment_id*/ None, req,
)
.await
.unwrap();
if cfg!(target_os = "macos") {
assert_eq!(response.status(), StatusCode::FORBIDDEN);
@@ -1285,9 +1344,11 @@ mod tests {
.expect("request should build");
req.extensions_mut().insert(state);
let response = http_plain_proxy(/*policy_decider*/ None, req)
.await
.unwrap();
let response = http_plain_proxy(
/*policy_decider*/ None, /*environment_id*/ None, req,
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::BAD_GATEWAY);
}
@@ -1309,9 +1370,11 @@ mod tests {
.unwrap();
req.extensions_mut().insert(state);
let response = http_connect_accept(/*policy_decider*/ None, req)
.await
.unwrap_err();
let response = http_connect_accept(
/*policy_decider*/ None, /*environment_id*/ None, req,
)
.await
.unwrap_err();
assert_eq!(response.status(), StatusCode::FORBIDDEN);
assert_eq!(
response.headers().get("x-proxy-error").unwrap(),
@@ -1332,7 +1395,10 @@ mod tests {
.unwrap();
req.extensions_mut().insert(state);
let response = http_plain_proxy(/*policy_decider*/ None, req).await;
let response = http_plain_proxy(
/*policy_decider*/ None, /*environment_id*/ None, req,
)
.await;
assert_eq!(response.unwrap().status(), StatusCode::BAD_REQUEST);
}

View File

@@ -222,11 +222,13 @@ impl NetworkProxyBuilder {
http_addr,
socks_addr,
socks_enabled: current_cfg.network.enable_socks5,
socks5_udp_enabled: current_cfg.network.enable_socks5_udp,
runtime_settings: Arc::new(RwLock::new(NetworkProxyRuntimeSettings::from_config(
&current_cfg,
)?)),
reserved_listeners,
policy_decider: self.policy_decider,
environment_proxies: Arc::new(Mutex::new(HashMap::new())),
})
}
}
@@ -323,15 +325,29 @@ impl NetworkProxyRuntimeSettings {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct EnvironmentProxyAddrs {
http_addr: SocketAddr,
socks_addr: SocketAddr,
}
struct EnvironmentProxy {
addrs: EnvironmentProxyAddrs,
http_task: JoinHandle<Result<()>>,
socks_task: Option<JoinHandle<Result<()>>>,
}
#[derive(Clone)]
pub struct NetworkProxy {
state: Arc<NetworkProxyState>,
http_addr: SocketAddr,
socks_addr: SocketAddr,
socks_enabled: bool,
socks5_udp_enabled: bool,
runtime_settings: Arc<RwLock<NetworkProxyRuntimeSettings>>,
reserved_listeners: Option<Arc<ReservedListeners>>,
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
environment_proxies: Arc<Mutex<HashMap<String, EnvironmentProxy>>>,
}
impl std::fmt::Debug for NetworkProxy {
@@ -640,20 +656,135 @@ impl NetworkProxy {
})
}
pub fn apply_to_env(&self, env: &mut HashMap<String, String>) {
fn apply_to_env_for_addrs(
&self,
env: &mut HashMap<String, String>,
addrs: EnvironmentProxyAddrs,
) {
let runtime_settings = self.runtime_settings();
// Enforce proxying for child processes. Proxy endpoint values are always rewritten;
// managed MITM CA vars preserve child-scoped overrides after proxy startup.
apply_proxy_env_overrides(
env,
self.http_addr,
self.socks_addr,
addrs.http_addr,
addrs.socks_addr,
self.socks_enabled,
runtime_settings.allow_local_binding,
runtime_settings.mitm_ca_trust_bundle.as_ref(),
);
}
pub fn apply_to_env(&self, env: &mut HashMap<String, String>) {
self.apply_to_env_for_addrs(
env,
EnvironmentProxyAddrs {
http_addr: self.http_addr,
socks_addr: self.socks_addr,
},
);
}
pub fn apply_to_env_for_environment(
&self,
env: &mut HashMap<String, String>,
environment_id: &str,
) -> Result<()> {
let addrs = self.environment_proxy_addrs(environment_id)?;
self.apply_to_env_for_addrs(env, addrs);
Ok(())
}
pub fn apply_to_env_for_optional_environment(
&self,
env: &mut HashMap<String, String>,
environment_id: Option<&str>,
) -> Result<()> {
match environment_id {
Some(environment_id) => self.apply_to_env_for_environment(env, environment_id),
None => {
self.apply_to_env(env);
Ok(())
}
}
}
fn environment_proxy_addrs(&self, environment_id: &str) -> Result<EnvironmentProxyAddrs> {
let mut proxies = self
.environment_proxies
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(proxy) = proxies.get(environment_id) {
return Ok(proxy.addrs);
}
let runtime = tokio::runtime::Handle::try_current().with_context(|| {
format!("failed to create network proxy for environment `{environment_id}`")
})?;
let listeners =
reserve_loopback_ephemeral_listeners(self.socks_enabled).with_context(|| {
format!("failed to reserve network proxy for environment `{environment_id}`")
})?;
let http_addr = listeners.http_addr().with_context(|| {
format!("failed to read HTTP proxy address for environment `{environment_id}`")
})?;
let socks_addr = listeners.socks_addr(self.socks_addr).with_context(|| {
format!("failed to read SOCKS proxy address for environment `{environment_id}`")
})?;
let addrs = EnvironmentProxyAddrs {
http_addr,
socks_addr,
};
let ReservedListenerSet {
http_listener,
socks_listener,
} = listeners;
let environment_id = environment_id.to_string();
let http_state = self.state.clone();
let http_decider = self.policy_decider.clone();
let http_environment_id = Some(environment_id.clone());
let http_task = runtime.spawn(async move {
http_proxy::run_http_proxy_with_std_listener(
http_state,
http_listener,
http_decider,
http_environment_id,
)
.await
});
let socks_task = if self.socks_enabled {
let socks_state = self.state.clone();
let socks_decider = self.policy_decider.clone();
let socks_environment_id = Some(environment_id.clone());
let socks5_udp_enabled = self.socks5_udp_enabled;
socks_listener.map(|listener| {
runtime.spawn(async move {
socks5::run_socks5_with_std_listener(
socks_state,
listener,
socks_decider,
socks_environment_id,
socks5_udp_enabled,
)
.await
})
})
} else {
None
};
proxies.insert(
environment_id,
EnvironmentProxy {
addrs,
http_task,
socks_task,
},
);
Ok(addrs)
}
pub async fn replace_config_state(&self, new_state: ConfigState) -> Result<()> {
let current_cfg = self.state.current_cfg().await?;
anyhow::ensure!(
@@ -717,10 +848,23 @@ impl NetworkProxy {
let http_task = tokio::spawn(async move {
match http_listener {
Some(listener) => {
http_proxy::run_http_proxy_with_std_listener(http_state, listener, http_decider)
.await
http_proxy::run_http_proxy_with_std_listener(
http_state,
listener,
http_decider,
/*environment_id*/ None,
)
.await
}
None => {
http_proxy::run_http_proxy(
http_state,
http_addr,
http_decider,
/*environment_id*/ None,
)
.await
}
None => http_proxy::run_http_proxy(http_state, http_addr, http_decider).await,
}
});
@@ -736,6 +880,7 @@ impl NetworkProxy {
socks_state,
listener,
socks_decider,
/*environment_id*/ None,
enable_socks5_udp,
)
.await
@@ -745,6 +890,7 @@ impl NetworkProxy {
socks_state,
socks_addr,
socks_decider,
/*environment_id*/ None,
enable_socks5_udp,
)
.await
@@ -758,6 +904,7 @@ impl NetworkProxy {
Ok(NetworkProxyHandle {
http_task: Some(http_task),
socks_task,
environment_proxies: self.environment_proxies.clone(),
completed: false,
})
}
@@ -766,6 +913,7 @@ impl NetworkProxy {
pub struct NetworkProxyHandle {
http_task: Option<JoinHandle<Result<()>>>,
socks_task: Option<JoinHandle<Result<()>>>,
environment_proxies: Arc<Mutex<HashMap<String, EnvironmentProxy>>>,
completed: bool,
}
@@ -774,6 +922,7 @@ impl NetworkProxyHandle {
Self {
http_task: Some(tokio::spawn(async { Ok(()) })),
socks_task: None,
environment_proxies: Arc::new(Mutex::new(HashMap::new())),
completed: true,
}
}
@@ -787,6 +936,7 @@ impl NetworkProxyHandle {
None => None,
};
self.completed = true;
abort_environment_proxies(self.environment_proxies.clone()).await;
http_result??;
if let Some(socks_result) = socks_result {
socks_result??;
@@ -796,6 +946,7 @@ impl NetworkProxyHandle {
pub async fn shutdown(mut self) -> Result<()> {
abort_tasks(self.http_task.take(), self.socks_task.take()).await;
abort_environment_proxies(self.environment_proxies.clone()).await;
self.completed = true;
Ok(())
}
@@ -816,6 +967,21 @@ async fn abort_tasks(
abort_task(socks_task).await;
}
async fn abort_environment_proxies(
environment_proxies: Arc<Mutex<HashMap<String, EnvironmentProxy>>>,
) {
let proxies = {
let mut guard = environment_proxies
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
guard.drain().map(|(_, proxy)| proxy).collect::<Vec<_>>()
};
for proxy in proxies {
abort_task(Some(proxy.http_task)).await;
abort_task(proxy.socks_task).await;
}
}
impl Drop for NetworkProxyHandle {
fn drop(&mut self) {
if self.completed {
@@ -823,8 +989,10 @@ impl Drop for NetworkProxyHandle {
}
let http_task = self.http_task.take();
let socks_task = self.socks_task.take();
let environment_proxies = self.environment_proxies.clone();
tokio::spawn(async move {
abort_tasks(http_task, socks_task).await;
abort_environment_proxies(environment_proxies).await;
});
}
}
@@ -905,6 +1073,33 @@ mod tests {
);
}
#[tokio::test]
async fn apply_to_env_for_environment_uses_distinct_proxy_ports() -> Result<()> {
let state = Arc::new(network_proxy_state_for_policy(
NetworkProxySettings::default(),
));
let proxy = NetworkProxy::builder().state(state).build().await?;
let handle = proxy.run().await?;
let mut local_env = HashMap::new();
proxy.apply_to_env_for_environment(&mut local_env, "local")?;
let mut remote_env = HashMap::new();
proxy.apply_to_env_for_environment(&mut remote_env, "remote")?;
assert_ne!(local_env.get("HTTP_PROXY"), remote_env.get("HTTP_PROXY"));
assert_ne!(
local_env.get("HTTP_PROXY"),
Some(&format!("http://{}", proxy.http_addr()))
);
assert_ne!(
remote_env.get("HTTP_PROXY"),
Some(&format!("http://{}", proxy.http_addr()))
);
handle.shutdown().await?;
Ok(())
}
#[tokio::test]
async fn managed_proxy_builder_does_not_reserve_socks_listener_when_disabled() {
let settings = NetworkProxySettings {

View File

@@ -64,6 +64,7 @@ pub async fn run_socks5(
state: Arc<NetworkProxyState>,
addr: SocketAddr,
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
environment_id: Option<String>,
enable_socks5_udp: bool,
) -> Result<()> {
let listener = TcpListener::build()
@@ -74,24 +75,40 @@ pub async fn run_socks5(
.map_err(anyhow::Error::from)
.with_context(|| format!("bind SOCKS5 proxy: {addr}"))?;
run_socks5_with_listener(state, listener, policy_decider, enable_socks5_udp).await
run_socks5_with_listener(
state,
listener,
policy_decider,
environment_id,
enable_socks5_udp,
)
.await
}
pub async fn run_socks5_with_std_listener(
state: Arc<NetworkProxyState>,
listener: StdTcpListener,
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
environment_id: Option<String>,
enable_socks5_udp: bool,
) -> Result<()> {
let listener =
TcpListener::try_from(listener).context("convert std listener to SOCKS5 proxy listener")?;
run_socks5_with_listener(state, listener, policy_decider, enable_socks5_udp).await
run_socks5_with_listener(
state,
listener,
policy_decider,
environment_id,
enable_socks5_udp,
)
.await
}
async fn run_socks5_with_listener(
state: Arc<NetworkProxyState>,
listener: TcpListener,
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
environment_id: Option<String>,
enable_socks5_udp: bool,
) -> Result<()> {
let addr = listener
@@ -115,10 +132,12 @@ async fn run_socks5_with_listener(
let tcp_connector = TargetCheckedTcpConnector::new(state.clone());
let policy_tcp_connector = service_fn({
let policy_decider = policy_decider.clone();
let environment_id = environment_id.clone();
move |req: TcpRequest| {
let tcp_connector = tcp_connector.clone();
let policy_decider = policy_decider.clone();
async move { handle_socks5_tcp(req, tcp_connector, policy_decider).await }
let environment_id = environment_id.clone();
async move { handle_socks5_tcp(req, tcp_connector, policy_decider, environment_id).await }
}
});
@@ -131,13 +150,18 @@ async fn run_socks5_with_listener(
if enable_socks5_udp {
let udp_state = state.clone();
let udp_decider = policy_decider.clone();
let udp_relay = DefaultUdpRelay::default().with_async_inspector(service_fn({
move |request: RelayRequest| {
let udp_state = udp_state.clone();
let udp_decider = udp_decider.clone();
async move { inspect_socks5_udp(request, udp_state, udp_decider).await }
}
}));
let udp_relay =
DefaultUdpRelay::default().with_async_inspector(service_fn({
let environment_id = environment_id.clone();
move |request: RelayRequest| {
let udp_state = udp_state.clone();
let udp_decider = udp_decider.clone();
let environment_id = environment_id.clone();
async move {
inspect_socks5_udp(request, udp_state, udp_decider, environment_id).await
}
}
}));
let socks_acceptor = base.with_udp_associator(udp_relay);
listener
.serve(AddInputExtensionLayer::new(state).into_layer(socks_acceptor))
@@ -154,6 +178,7 @@ async fn handle_socks5_tcp(
req: TcpRequest,
tcp_connector: TargetCheckedTcpConnector,
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
environment_id: Option<String>,
) -> Result<EstablishedClientConnection<Socks5TcpConnection, TcpRequest>, BoxError> {
let app_state = req
.extensions()
@@ -268,7 +293,7 @@ async fn handle_socks5_tcp(
protocol: NetworkProtocol::Socks5Tcp,
host: host.clone(),
port,
environment_id: None,
environment_id,
client_addr: client.clone(),
method: None,
command: None,
@@ -519,6 +544,7 @@ async fn inspect_socks5_udp(
request: RelayRequest,
state: Arc<NetworkProxyState>,
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
environment_id: Option<String>,
) -> io::Result<RelayResponse> {
let RelayRequest {
server_address,
@@ -625,7 +651,7 @@ async fn inspect_socks5_udp(
protocol: NetworkProtocol::Socks5Udp,
host: host.clone(),
port,
environment_id: None,
environment_id,
client_addr: client.clone(),
method: None,
command: None,
@@ -783,6 +809,7 @@ mod tests {
request,
TargetCheckedTcpConnector::new(state.clone()),
/*policy_decider*/ None,
/*environment_id*/ None,
)
.await
})
@@ -826,6 +853,7 @@ mod tests {
request,
TargetCheckedTcpConnector::new(state),
/*policy_decider*/ None,
/*environment_id*/ None,
)
.await
.expect("limited-mode HTTPS should use MITM");
@@ -851,6 +879,7 @@ mod tests {
request,
TargetCheckedTcpConnector::new(state),
/*policy_decider*/ None,
/*environment_id*/ None,
)
.await
})
@@ -896,6 +925,7 @@ mod tests {
request,
TargetCheckedTcpConnector::new(state),
/*policy_decider*/ None,
/*environment_id*/ None,
)
.await
.expect_err("limited-mode HTTPS requires MITM");
@@ -933,6 +963,7 @@ mod tests {
request,
TargetCheckedTcpConnector::new(state),
/*policy_decider*/ None,
/*environment_id*/ None,
)
.await
.expect("hooked HTTPS should use MITM");
@@ -967,6 +998,7 @@ mod tests {
request,
TargetCheckedTcpConnector::new(state),
/*policy_decider*/ None,
/*environment_id*/ None,
)
.await
.expect_err("hooked non-HTTPS SOCKS should require MITM");
@@ -992,7 +1024,10 @@ mod tests {
};
let (result, events) = capture_events(|| async {
inspect_socks5_udp(request, state, /*policy_decider*/ None).await
inspect_socks5_udp(
request, state, /*policy_decider*/ None, /*environment_id*/ None,
)
.await
})
.await;
assert!(result.is_err(), "limited-mode UDP request should be denied");