diff --git a/crates/nono/src/capability.rs b/crates/nono/src/capability.rs index e786b7d83..cfee53d4b 100644 --- a/crates/nono/src/capability.rs +++ b/crates/nono/src/capability.rs @@ -1066,8 +1066,16 @@ impl CapabilitySet { /// `127.0.0.1:port`. Works across all network modes. /// /// On macOS: outbound is per-port via Seatbelt; bind/inbound is blanket - /// (same tradeoff as `--allow-bind`). - /// On Linux: per-port ConnectTcp + BindTcp via Landlock. + /// (same tradeoff as `--allow-bind`). Port `0` generates a `localhost:*` + /// wildcard rule, allowing any localhost port. + /// + /// On Linux: per-port ConnectTcp + BindTcp via Landlock. Port `0` has no + /// wildcard semantics in Landlock — `ConnectTcp/0` only matches connects to + /// destination port 0, and `BindTcp/0` only permits `bind(port=0)` (OS ephemeral + /// assignment). Neither covers connecting to a random high port such as a + /// Testcontainers mapping. Instead, port `0` causes Landlock network handling to be + /// skipped entirely, leaving the proxy as the sole domain enforcer. This is only + /// permitted in `ProxyOnly` mode; `Blocked` mode rejects port `0` at sandbox init. #[must_use] pub fn allow_localhost_port(mut self, port: u16) -> Self { self.localhost_ports.push(port); diff --git a/crates/nono/src/sandbox/linux.rs b/crates/nono/src/sandbox/linux.rs index a6c98ea48..526cae894 100644 --- a/crates/nono/src/sandbox/linux.rs +++ b/crates/nono/src/sandbox/linux.rs @@ -479,14 +479,49 @@ pub fn apply_with_abi(caps: &CapabilitySet, abi: &DetectedAbi) -> Result Result Result Result= 0, "fork() failed"); + + if child_pid == 0 { + let exit_code = match apply_with_abi(&caps, &detected) { + Ok(_) => 0, + Err(_) => 1, + }; + // SAFETY: _exit terminates the child without running parent destructors. + unsafe { libc::_exit(exit_code) }; + } + + let mut child_status = 0; + // SAFETY: child_pid is the PID returned by fork() in the parent branch. + let waited = unsafe { libc::waitpid(child_pid, &mut child_status, 0) }; + assert_eq!(waited, child_pid, "waitpid() failed"); + assert!(libc::WIFEXITED(child_status), "child did not exit normally"); + assert_eq!( + libc::WEXITSTATUS(child_status), + 0, + "port 0 in ProxyOnly mode must be accepted on Linux" + ); } #[test]