use heel::{DenyAll, Sandbox};
fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
println!("=== Linux Sandbox Security Test ===\n");
let sandbox = smol::block_on(Sandbox::<DenyAll>::new())?;
println!("Sandbox created successfully");
println!("Working dir: {:?}\n", sandbox.working_dir());
println!("Test 1: Reading /etc/shadow (should fail)");
let output = smol::block_on(sandbox.command("cat").args(["/etc/shadow"]).output())?;
println!(" Exit code: {:?}", output.status.code());
println!(" Stderr: {}", String::from_utf8_lossy(&output.stderr));
assert!(
!output.status.success(),
"SECURITY FAILURE: Was able to read /etc/shadow!"
);
println!(" PASS: Cannot read /etc/shadow\n");
println!("Test 2: Reading /etc/passwd (should succeed)");
let output = smol::block_on(
sandbox
.command("head")
.args(["-n", "1", "/etc/passwd"])
.output(),
)?;
println!(" Exit code: {:?}", output.status.code());
println!(
" Stdout: {}",
String::from_utf8_lossy(&output.stdout).trim()
);
assert!(
output.status.success(),
"Should be able to read /etc/passwd"
);
println!(" PASS: Can read /etc/passwd\n");
println!("Test 3: Creating UDP socket (should fail)");
let output = smol::block_on(
sandbox
.command("python3")
.args(["-c", "import socket; s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); print('UDP socket created')"])
.output(),
)?;
println!(" Exit code: {:?}", output.status.code());
println!(
" Stderr: {}",
String::from_utf8_lossy(&output.stderr)
.lines()
.take(3)
.collect::<Vec<_>>()
.join("\n ")
);
assert!(
!output.status.success(),
"SECURITY FAILURE: Was able to create UDP socket!"
);
println!(" PASS: UDP socket blocked\n");
println!("Test 4: Creating raw socket (should fail)");
let output = smol::block_on(
sandbox
.command("python3")
.args(["-c", "import socket; s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP); print('Raw socket created')"])
.output(),
)?;
println!(" Exit code: {:?}", output.status.code());
assert!(
!output.status.success(),
"SECURITY FAILURE: Was able to create raw socket!"
);
println!(" PASS: Raw socket blocked\n");
println!("Test 5: Creating TCP socket (should succeed)");
let output = smol::block_on(
sandbox
.command("python3")
.args(["-c", "import socket; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); print('TCP socket created'); s.close()"])
.output(),
)?;
println!(" Exit code: {:?}", output.status.code());
println!(
" Stdout: {}",
String::from_utf8_lossy(&output.stdout).trim()
);
assert!(output.status.success(), "TCP sockets should work");
println!(" PASS: TCP socket works\n");
println!("Test 6: Direct network access (should fail due to Landlock)");
let output = smol::block_on(
sandbox
.command("python3")
.args([
"-c",
r#"
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
try:
s.connect(('8.8.8.8', 53))
print('Connected to 8.8.8.8:53')
except Exception as e:
print(f'Connection blocked: {e}')
exit(1)
"#,
])
.output(),
)?;
println!(" Exit code: {:?}", output.status.code());
println!(
" Output: {}",
String::from_utf8_lossy(&output.stdout).trim()
);
println!(" PASS: Direct TCP connection restricted\n");
println!("Test 7: Writing to working directory (should succeed)");
let output = smol::block_on(
sandbox
.command("sh")
.args([
"-c",
"echo 'test content' > test_file.txt && cat test_file.txt",
])
.output(),
)?;
println!(" Exit code: {:?}", output.status.code());
println!(
" Stdout: {}",
String::from_utf8_lossy(&output.stdout).trim()
);
assert!(
output.status.success(),
"Should be able to write to working directory"
);
println!(" PASS: Can write to working directory\n");
println!("Test 8: ptrace syscall (should fail)");
let output = smol::block_on(
sandbox
.command("strace")
.args(["-e", "trace=write", "echo", "test"])
.output(),
)?;
println!(" Exit code: {:?}", output.status.code());
if !output.status.success() {
println!(" PASS: ptrace blocked (strace failed)\n");
} else {
println!(" Note: strace succeeded (may not have ptrace in this env)\n");
}
println!("=== All Security Tests Passed ===");
Ok(())
}