#![cfg(any(target_os = "linux", target_os = "macos"))]
use std::process::Command;
fn python() -> Option<&'static str> {
["python3", "python"].into_iter().find(|cand| {
Command::new(cand)
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
})
}
#[cfg(target_os = "linux")]
const MAKE_INET_SOCKET: &str = "import socket; socket.socket(socket.AF_INET, socket.SOCK_STREAM)";
#[test]
#[cfg(target_os = "linux")]
#[ignore = "spawns the real binary + python3; run with: cargo test --test sandbox_network -- --ignored"]
fn no_network_blocks_inet_socket_but_allows_it_otherwise() {
let Some(py) = python() else {
eprintln!("skipping: no python interpreter on PATH");
return;
};
let bin = env!("CARGO_BIN_EXE_mermaid");
let denied = Command::new(bin)
.args([
"__sandbox-exec",
"--no-network",
"--",
py,
"-c",
MAKE_INET_SOCKET,
])
.output()
.expect("spawn sandboxed python");
assert!(
!denied.status.success(),
"AF_INET socket should be denied under --no-network (status={:?})",
denied.status
);
let allowed = Command::new(bin)
.args(["__sandbox-exec", "--", py, "-c", MAKE_INET_SOCKET])
.output()
.expect("spawn unsandboxed python");
assert!(
allowed.status.success(),
"AF_INET socket should succeed without --no-network (stderr={})",
String::from_utf8_lossy(&allowed.stderr)
);
}
#[test]
#[ignore = "spawns the real binary + python3; run with: cargo test --test sandbox_network -- --ignored"]
fn no_network_blocks_tcp_connect_but_allows_it_otherwise() {
let Some(py) = python() else {
eprintln!("skipping: no python interpreter on PATH");
return;
};
let bin = env!("CARGO_BIN_EXE_mermaid");
let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind local listener");
let port = listener.local_addr().expect("local addr").port();
let connect = format!(
"import socket; socket.create_connection((\"127.0.0.1\", {port}), timeout=5).close()"
);
let denied = Command::new(bin)
.args(["__sandbox-exec", "--no-network", "--", py, "-c", &connect])
.output()
.expect("spawn sandboxed python");
assert!(
!denied.status.success(),
"TCP connect should be denied under --no-network (status={:?}, stderr={})",
denied.status,
String::from_utf8_lossy(&denied.stderr)
);
let allowed = Command::new(bin)
.args(["__sandbox-exec", "--", py, "-c", &connect])
.output()
.expect("spawn unsandboxed python");
assert!(
allowed.status.success(),
"TCP connect should succeed without --no-network (stderr={})",
String::from_utf8_lossy(&allowed.stderr)
);
}
#[test]
#[ignore = "spawns the real binary; run with: cargo test --test sandbox_network -- --ignored"]
fn no_network_still_allows_ordinary_local_commands() {
let bin = env!("CARGO_BIN_EXE_mermaid");
let output = Command::new(bin)
.args([
"__sandbox-exec",
"--no-network",
"--",
"sh",
"-c",
"echo ok && ls / >/dev/null",
])
.output()
.expect("spawn sandboxed shell");
assert!(
output.status.success(),
"local command should run under --no-network (stderr={})",
String::from_utf8_lossy(&output.stderr)
);
assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "ok");
}