use assert_cmd::prelude::*;
use std::process::Command;
use tempfile::tempdir;
#[cfg(unix)]
fn shell_single_quote(value: &str) -> String {
format!("'{}'", value.replace('\'', "'\\''"))
}
#[test]
fn test_profile_cli_workflow() -> anyhow::Result<()> {
let tmp = tempdir()?;
let out_yaml = tmp.path().join("policy.yaml");
let out_report = tmp.path().join("report.md");
let out_evidence = tmp.path().join("policy.evidence.yaml");
let mut cmd = Command::new(assert_cmd::cargo_bin!("assay"));
cmd.arg("sandbox")
.arg("--profile")
.arg(&out_yaml)
.arg("--profile-report")
.arg(&out_report)
.arg("--quiet")
.arg("--")
.arg("true");
let output = cmd.unwrap();
assert!(output.status.success());
assert!(out_yaml.exists());
assert!(out_report.exists());
assert!(out_evidence.exists());
let yaml_content = std::fs::read_to_string(out_yaml)?;
let policy: serde_yaml::Value = serde_yaml::from_str(&yaml_content)?;
assert_eq!(policy["api_version"], 1);
assert!(
policy["extends"].as_sequence().is_some_and(Vec::is_empty),
"generated YAML policies must declare an empty extends list"
);
let evidence_content = std::fs::read_to_string(out_evidence)?;
assert!(
evidence_content.contains("version: '1.0'")
|| evidence_content.contains("version: \"1.0\"")
);
assert!(evidence_content.contains("total_runs: 1"));
assert!(
!evidence_content.contains("sandbox_degradations:"),
"healthy run should not emit sandbox degradation observations"
);
Ok(())
}
#[test]
fn test_profile_json_output() -> anyhow::Result<()> {
let tmp = tempdir()?;
let out_json = tmp.path().join("policy.json");
let out_evidence = tmp.path().join("policy.evidence.json");
let mut cmd = Command::new(assert_cmd::cargo_bin!("assay"));
cmd.arg("sandbox")
.arg("--profile")
.arg(&out_json)
.arg("--profile-format")
.arg("json")
.arg("--quiet")
.arg("--")
.arg("true");
let output = cmd.unwrap();
assert!(output.status.success());
assert!(out_json.exists());
assert!(out_evidence.exists());
let json_content = std::fs::read_to_string(out_json)?;
let policy: serde_json::Value = serde_json::from_str(&json_content)?;
assert_eq!(policy["api_version"], 1);
assert!(
policy["extends"].as_array().is_some_and(Vec::is_empty),
"generated JSON policies must declare an empty extends list"
);
Ok(())
}
#[test]
#[cfg(unix)]
fn owasp_mcp05_sandbox_keeps_shell_metacharacters_as_argv() -> anyhow::Result<()> {
let tmp = tempdir()?;
let sentinel = tmp.path().join("injection-sentinel");
let quoted_sentinel = shell_single_quote(&sentinel.to_string_lossy());
let injection_arg = format!("; touch {quoted_sentinel}");
let mut cmd = Command::new(assert_cmd::cargo_bin!("assay"));
cmd.arg("sandbox")
.arg("--quiet")
.arg("--")
.arg("sh")
.arg("-c")
.arg("printf safe")
.arg(&injection_arg);
let output = cmd.unwrap();
assert!(output.status.success());
assert_eq!(String::from_utf8_lossy(&output.stdout), "safe");
assert!(
!sentinel.exists(),
"sandbox command construction executed shell metacharacters from argv"
);
Ok(())
}