use std::path::Path;
use harn_vm::orchestration::{CapabilityPolicy, ProcessSandboxPolicy, SandboxProfile};
pub(crate) struct ConformanceWriteRoot;
impl Drop for ConformanceWriteRoot {
fn drop(&mut self) {
harn_vm::orchestration::pop_execution_policy();
}
}
impl ConformanceWriteRoot {
pub(crate) fn install(state_dir: &Path) -> Self {
harn_vm::orchestration::push_execution_policy(case_policy(state_dir));
Self
}
}
fn case_policy(state_dir: &Path) -> CapabilityPolicy {
let mut workspace_roots = vec![root_string(state_dir), root_string(&std::env::temp_dir())];
workspace_roots.sort();
workspace_roots.dedup();
CapabilityPolicy {
workspace_roots,
read_only_roots: filesystem_roots(),
process_sandbox: ProcessSandboxPolicy {
read_roots: runner_roots(),
write_roots: runner_roots(),
..ProcessSandboxPolicy::default()
},
sandbox_profile: SandboxProfile::WorkspacePaths,
..CapabilityPolicy::default()
}
}
fn runner_roots() -> Vec<String> {
std::env::current_dir()
.map(|dir| vec![root_string(&dir)])
.unwrap_or_default()
}
fn filesystem_roots() -> Vec<String> {
let anchors = [
std::env::current_dir().ok(),
Some(std::env::temp_dir()),
std::env::current_exe().ok(),
];
let mut roots: Vec<String> = anchors
.into_iter()
.flatten()
.filter_map(|path| path.ancestors().last().map(Path::to_path_buf))
.map(|root| root.display().to_string())
.collect();
roots.sort();
roots.dedup();
roots
}
fn root_string(path: &Path) -> String {
path.display().to_string()
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
#[test]
fn the_case_owns_its_state_dir_but_not_its_fixture_directory() {
let policy = case_policy(Path::new("/tmp/harn-conformance-state-x/.harn"));
assert!(policy
.workspace_roots
.iter()
.any(|root| root == "/tmp/harn-conformance-state-x/.harn"));
assert!(
!policy
.workspace_roots
.iter()
.any(|root| root.contains("conformance/tests")),
"a fixture directory is not the case's to write: {:?}",
policy.workspace_roots
);
}
#[test]
fn reads_are_not_confined() {
let policy = case_policy(Path::new("/state/.harn"));
assert!(
!policy.read_only_roots.is_empty(),
"a case must still be able to read the repository and the toolchain"
);
for root in &policy.read_only_roots {
assert_eq!(
Path::new(root).ancestors().last().map(Path::to_path_buf),
Some(PathBuf::from(root)),
"read roots are filesystem roots, not curated directories"
);
}
}
#[test]
fn subprocesses_may_still_start_from_the_runners_directory() {
let policy = case_policy(Path::new("/state/.harn"));
let cwd = root_string(&std::env::current_dir().unwrap());
assert!(
policy.process_sandbox.read_roots.contains(&cwd)
&& policy.process_sandbox.write_roots.contains(&cwd),
"cases shell out from the runner's directory: {:?}",
policy.process_sandbox
);
assert!(
!policy.workspace_roots.contains(&cwd),
"the runner's directory is launchable, not writable: {:?}",
policy.workspace_roots
);
}
#[test]
fn the_profile_confines_writes_but_not_subprocesses() {
let profile = case_policy(Path::new("/state/.harn")).sandbox_profile;
assert!(
profile.enforces_path_scope(),
"a stray write is the failure this module exists to catch"
);
assert!(
!profile.confines_processes(),
"cases legitimately shell out; OS confinement would deny them \
without closing the write hole"
);
}
}