//! The executor end to end on this host: what it lets through, what it //! denies, the mechanism it commits to, and the rung `Host::run_and_seal` //! seals. Where the probe finds no confinement (any host but macOS, or a //! macOS host whose sandbox did not hold), the same tests assert the S0 path //! instead, so neither branch passes by skipping. use ed25519_dalek::SigningKey; use sandbox_attest::{Mechanism, Outcome}; use sandbox_capability::{ComputeGrant, Grants, HostGrant}; use sandbox_exec_os::OsExecutor; use sandbox_runtime::{Executor, Host, RunError, TierRequest, Unit}; use sandbox_schema::Tier; use std::path::{Path, PathBuf}; /// A scratch directory of its own for each test. fn scratch(name: &str) -> PathBuf { let d = std::env::temp_dir().join(format!("sandbox-exec-os-{name}-{}", std::process::id())); let _ = std::fs::remove_dir_all(&d); std::fs::create_dir_all(&d).unwrap(); d.canonicalize().unwrap() } fn grants(read: &Path, write: &Path) -> Grants { let mut g = Grants { compute: ComputeGrant::cpu_only(5_000_000, 200, 16 * 1024 * 1024), host: HostGrant { logging: true, clock: true, random: true, inference: false }, ..Grants::default() }; g.storage.read = vec![read.to_str().unwrap().into()]; g.storage.write = vec![write.to_str().unwrap().into()]; g } /// A granted read and a granted write go through; a file outside the grants /// does not, and the run that tried says so. At S1 the mechanism commits to /// the runtime and the very policy applied. #[test] fn it_reads_and_writes_what_was_granted_and_nothing_else() { let dir = scratch("rw"); let (input, output, secret) = (dir.join("in"), dir.join("out"), dir.join("secret")); std::fs::create_dir_all(&input).unwrap(); std::fs::create_dir_all(&output).unwrap(); std::fs::create_dir_all(&secret).unwrap(); std::fs::write(input.join("a.txt"), b"granted").unwrap(); std::fs::write(secret.join("s.txt"), b"not granted").unwrap(); let g = grants(&input, &output); let unit = Unit::new(b"cp", &g); let copy = OsExecutor::new( "/bin/cp", vec![input.join("a.txt").to_str().unwrap().into(), output.join("b.txt").to_str().unwrap().into()], &dir, ); let report = copy.run(&unit).expect("a run with enforceable grants"); assert_eq!(report.outcome, Outcome::Completed); assert_eq!(std::fs::read(output.join("b.txt")).unwrap(), b"granted"); let peek = OsExecutor::new("/bin/cat", vec![secret.join("s.txt").to_str().unwrap().into()], &dir); let report = peek.run(&unit).expect("the run itself starts"); match OsExecutor::confinement() { Some(c) => { eprintln!("sandbox-exec-os: {} denied a read outside the grants", c.runtime); assert_eq!(report.outcome, Outcome::Failed, "a file outside the grants was read"); let policy = peek.policy(&g).unwrap(); let m = peek.mechanism().expect("a confined run commits to its mechanism"); assert_eq!(m, Mechanism::commit(c.runtime.clone(), policy.as_bytes(), &[])); assert!(m.runtime.starts_with("seatbelt/")); } None => { eprintln!("sandbox-exec-os: no confinement here; the run is S0 and unconfined"); assert_eq!(report.outcome, Outcome::Completed, "unconfined, the read succeeds"); assert_eq!(peek.mechanism(), None, "no mechanism at S0"); } } let _ = std::fs::remove_dir_all(&dir); } /// The canonical host path: a trusted unit that writes storage needs exactly /// S1. Where the confinement holds, the run is sealed at S1 with its /// mechanism; where it does not, the host refuses to seal it at all. #[test] fn run_and_seal_seals_s1_only_where_the_confinement_holds() { let dir = scratch("seal"); let (input, output) = (dir.join("in"), dir.join("out")); std::fs::create_dir_all(&input).unwrap(); std::fs::create_dir_all(&output).unwrap(); std::fs::write(input.join("a.txt"), b"granted").unwrap(); let g = grants(&input, &output); let unit = Unit::new(b"cp", &g); let exec = OsExecutor::new( "/bin/cp", vec![input.join("a.txt").to_str().unwrap().into(), output.join("b.txt").to_str().unwrap().into()], &dir, ); let supervisor = SigningKey::from_bytes(&[42u8; 32]); let host = Host::new("sandbox-exec-os", "steward.os", SigningKey::from_bytes(&[41u8; 32]), supervisor.clone()); let req = TierRequest { trusted: true, ..TierRequest::minimal(&g) }; let sealed = host.run_and_seal(&exec, &unit, &req, 1, [0; 8], 1, 2); match OsExecutor::confinement() { Some(_) => { let sealed = sealed.expect("sealed at S1"); assert_eq!(sealed.attestation.tier, Tier::S1); assert!(sealed.attestation.mechanism.is_some(), "an S1 run carries its mechanism"); sealed.attestation.verify(&sealed.attestation_sig, &supervisor.verifying_key()).unwrap(); } None => { assert!( matches!(sealed, Err(RunError::TierMismatch { .. })), "an S0 executor must not be sealed for a unit that needs S1" ); } } let _ = std::fs::remove_dir_all(&dir); } /// Where the confinement holds, a unit whose grants it cannot enforce is /// refused before anything runs, rather than run with a capability its grant /// denies. #[test] fn a_unit_it_cannot_confine_is_refused_before_it_runs() { if OsExecutor::confinement().is_none() { eprintln!("sandbox-exec-os: no confinement here; nothing to refuse"); return; } let dir = scratch("refuse"); let marker = dir.join("ran"); let mut g = grants(&dir, &dir); g.host.clock = false; let unit = Unit::new(b"touch", &g); let exec = OsExecutor::new("/usr/bin/touch", vec![marker.to_str().unwrap().into()], &dir); assert!(exec.run(&unit).is_err(), "a grant withholding the clock cannot be enforced natively"); assert!(!marker.exists(), "the refused unit ran"); let _ = std::fs::remove_dir_all(&dir); }