use std::process::Command;
fn nono_bin() -> Command {
Command::new(env!("CARGO_BIN_EXE_nono"))
}
fn combined_output(output: &std::process::Output) -> String {
let mut s = String::from_utf8_lossy(&output.stdout).into_owned();
s.push_str(&String::from_utf8_lossy(&output.stderr));
s
}
#[test]
fn env_nono_allow_comma_separated() {
let dir = tempfile::tempdir().expect("tmpdir");
let path_a = dir.path().join("a");
let path_b = dir.path().join("b");
std::fs::create_dir(&path_a).expect("create dir a");
std::fs::create_dir(&path_b).expect("create dir b");
let allow_val = format!("{},{}", path_a.display(), path_b.display());
let output = nono_bin()
.env("NONO_ALLOW", &allow_val)
.args(["run", "--dry-run", "echo"])
.output()
.expect("failed to run nono");
let text = combined_output(&output);
let a_str = path_a.display().to_string();
let b_str = path_b.display().to_string();
assert!(
text.contains(a_str.as_str()) && text.contains(b_str.as_str()),
"expected both paths in dry-run output, got:\n{text}"
);
}
#[test]
fn env_nono_block_net() {
let output = nono_bin()
.env("NONO_BLOCK_NET", "1")
.args(["run", "--allow", "/tmp", "--dry-run", "echo"])
.output()
.expect("failed to run nono");
let text = combined_output(&output);
assert!(
text.contains("blocked"),
"expected network blocked in dry-run output, got:\n{text}"
);
}
#[test]
fn env_nono_block_net_accepts_true() {
let output = nono_bin()
.env("NONO_BLOCK_NET", "true")
.args(["run", "--allow", "/tmp", "--dry-run", "echo"])
.output()
.expect("failed to run nono");
assert!(
output.status.success(),
"NONO_BLOCK_NET=true should be accepted, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn legacy_env_nono_net_block_still_works() {
let output = nono_bin()
.env("NONO_NET_BLOCK", "1")
.args(["run", "--allow", "/tmp", "--dry-run", "echo"])
.output()
.expect("failed to run nono");
let text = combined_output(&output);
assert!(
text.contains("blocked"),
"expected legacy NONO_NET_BLOCK to still block network, got:\n{text}"
);
}
#[test]
fn env_nono_profile() {
let output = nono_bin()
.env("NONO_PROFILE", "node-dev")
.args(["run", "--dry-run", "--allow-cwd", "echo"])
.output()
.expect("failed to run nono");
assert!(
output.status.success(),
"NONO_PROFILE=node-dev should be accepted, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn env_nono_network_profile() {
let output = nono_bin()
.env("NONO_NETWORK_PROFILE", "node-dev")
.args(["run", "--allow", "/tmp", "--dry-run", "echo"])
.output()
.expect("failed to run nono");
assert!(
output.status.success(),
"NONO_NETWORK_PROFILE should be accepted, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn cli_flag_overrides_env_var() {
let output = nono_bin()
.env("NONO_PROFILE", "nonexistent-profile-from-env")
.args([
"run",
"--profile",
"node-dev",
"--dry-run",
"--allow-cwd",
"echo",
])
.output()
.expect("failed to run nono");
assert!(
output.status.success(),
"CLI --profile should override env var, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn env_nono_upstream_proxy() {
let output = nono_bin()
.env("NONO_UPSTREAM_PROXY", "squid.corp:3128")
.args(["run", "--allow", "/tmp", "--dry-run", "echo"])
.output()
.expect("failed to run nono");
assert!(
output.status.success(),
"NONO_UPSTREAM_PROXY should be accepted, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn env_nono_upstream_bypass_comma_separated() {
let output = nono_bin()
.env("NONO_UPSTREAM_PROXY", "squid.corp:3128")
.env("NONO_UPSTREAM_BYPASS", "internal.corp,*.private.net")
.args(["run", "--allow", "/tmp", "--dry-run", "echo"])
.output()
.expect("failed to run nono");
assert!(
output.status.success(),
"NONO_UPSTREAM_BYPASS should be accepted, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn env_nono_upstream_bypass_requires_upstream_proxy() {
let output = nono_bin()
.env("NONO_UPSTREAM_BYPASS", "internal.corp")
.args(["run", "--allow", "/tmp", "--dry-run", "echo"])
.output()
.expect("failed to run nono");
assert!(
!output.status.success(),
"NONO_UPSTREAM_BYPASS without NONO_UPSTREAM_PROXY should fail"
);
}
#[test]
fn env_allow_net_conflicts_with_upstream_proxy() {
let output = nono_bin()
.env("NONO_UPSTREAM_PROXY", "squid.corp:3128")
.env("NONO_ALLOW_NET", "true")
.args(["run", "--allow", "/tmp", "--dry-run", "echo"])
.output()
.expect("failed to run nono");
assert!(
!output.status.success(),
"NONO_ALLOW_NET + NONO_UPSTREAM_PROXY should conflict"
);
}
#[test]
fn allow_net_overrides_profile_external_proxy() {
let dir = tempfile::tempdir().expect("tmpdir");
let profile_path = dir.path().join("ext-proxy-profile.json");
std::fs::write(
&profile_path,
r#"{
"meta": { "name": "ext-proxy-test" },
"network": { "external_proxy": "squid.corp:3128" }
}"#,
)
.expect("write profile");
let output = nono_bin()
.args([
"run",
"--profile",
profile_path.to_str().expect("valid utf8"),
"--allow-net",
"--allow",
"/tmp",
"--dry-run",
"echo",
])
.output()
.expect("failed to run nono");
let text = combined_output(&output);
assert!(
output.status.success(),
"--allow-net should override profile external_proxy, stderr: {text}"
);
assert!(
text.contains("allowed"),
"expected unrestricted network in dry-run output, got:\n{text}"
);
}
#[test]
fn env_conflict_allow_net_and_block_net() {
let output = nono_bin()
.env("NONO_ALLOW_NET", "true")
.env("NONO_BLOCK_NET", "true")
.args(["run", "--allow", "/tmp", "--dry-run", "echo"])
.output()
.expect("failed to run nono");
assert!(
!output.status.success(),
"NONO_ALLOW_NET + NONO_BLOCK_NET should conflict"
);
}
#[test]
fn environment_allow_vars_with_profile() {
let dir = tempfile::tempdir().expect("tmpdir");
let profile_path = dir.path().join("env-filter-profile.json");
std::fs::write(
&profile_path,
r#"{
"meta": { "name": "env-filter-test" },
"filesystem": { "allow": ["/usr", "/bin", "/lib", "/tmp"] },
"environment": {
"allow_vars": ["PATH"]
}
}"#,
)
.expect("write profile");
let output = nono_bin()
.env("MY_SECRET", "should_not_see")
.args([
"run",
"--profile",
profile_path.to_str().expect("valid utf8"),
"--dry-run",
"echo",
])
.output()
.expect("failed to run nono");
assert!(
output.status.success(),
"profile with environment.allow_vars should be accepted, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn environment_allow_vars_default_allows_all() {
let dir = tempfile::tempdir().expect("tmpdir");
let profile_path = dir.path().join("no-env-filter-profile.json");
std::fs::write(
&profile_path,
r#"{
"meta": { "name": "no-env-filter-test" },
"filesystem": { "allow": ["/usr", "/bin", "/lib", "/tmp"] }
}"#,
)
.expect("write profile");
let output = nono_bin()
.args([
"run",
"--profile",
profile_path.to_str().expect("valid utf8"),
"--dry-run",
"echo",
])
.output()
.expect("failed to run nono");
assert!(
output.status.success(),
"profile without environment section should be accepted, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn environment_allow_vars_prefix_patterns() {
let dir = tempfile::tempdir().expect("tmpdir");
let profile_path = dir.path().join("env-prefix-profile.json");
std::fs::write(
&profile_path,
r#"{
"meta": { "name": "env-prefix-test" },
"filesystem": { "allow": ["/usr", "/bin", "/lib", "/tmp"] },
"environment": {
"allow_vars": ["PATH", "HOME", "AWS_*", "MYAPP_*"]
}
}"#,
)
.expect("write profile");
let output = nono_bin()
.args([
"run",
"--profile",
profile_path.to_str().expect("valid utf8"),
"--dry-run",
"echo",
])
.output()
.expect("failed to run nono");
assert!(
output.status.success(),
"profile with prefix patterns should be accepted, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn environment_allow_vars_bare_star() {
let dir = tempfile::tempdir().expect("tmpdir");
let profile_path = dir.path().join("env-bare-star-profile.json");
std::fs::write(
&profile_path,
r#"{
"meta": { "name": "env-bare-star-test" },
"filesystem": { "allow": ["/usr", "/bin", "/lib", "/tmp"] },
"environment": {
"allow_vars": ["*"]
}
}"#,
)
.expect("write profile");
let output = nono_bin()
.args([
"run",
"--profile",
profile_path.to_str().expect("valid utf8"),
"--dry-run",
"echo",
])
.output()
.expect("failed to run nono");
assert!(
output.status.success(),
"profile with bare * should be accepted, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}