use cuttlefish_host::caps::Capabilities;
use std::fs;
#[test]
fn allows_a_read_under_a_granted_root() {
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("ok.txt");
fs::write(&file, "hi").unwrap();
let caps = Capabilities::new(vec![dir.path().to_path_buf()]);
assert!(caps.allows_read(&file));
}
#[test]
fn allows_a_read_nested_deeper_than_the_root() {
let dir = tempfile::tempdir().unwrap();
let nested = dir.path().join("a/b/c");
fs::create_dir_all(&nested).unwrap();
let file = nested.join("deep.txt");
fs::write(&file, "hi").unwrap();
let caps = Capabilities::new(vec![dir.path().to_path_buf()]);
assert!(caps.allows_read(&file));
}
#[test]
fn denies_a_read_outside_every_granted_root() {
let granted = tempfile::tempdir().unwrap();
let other = tempfile::tempdir().unwrap();
let secret = other.path().join("secret.txt");
fs::write(&secret, "nope").unwrap();
let caps = Capabilities::new(vec![granted.path().to_path_buf()]);
assert!(!caps.allows_read(&secret));
}
#[test]
fn denies_traversal_out_of_a_granted_root() {
let root = tempfile::tempdir().unwrap();
let inner = root.path().join("inner");
fs::create_dir(&inner).unwrap();
let outside = root.path().join("outside.txt");
fs::write(&outside, "nope").unwrap();
let caps = Capabilities::new(vec![inner.clone()]);
assert!(!caps.allows_read(&inner.join("../outside.txt")));
}
#[cfg(unix)]
#[test]
fn denies_a_symlink_pointing_out_of_a_granted_root() {
let granted = tempfile::tempdir().unwrap();
let other = tempfile::tempdir().unwrap();
let secret = other.path().join("secret.txt");
fs::write(&secret, "nope").unwrap();
let link = granted.path().join("innocent.txt");
std::os::unix::fs::symlink(&secret, &link).unwrap();
let caps = Capabilities::new(vec![granted.path().to_path_buf()]);
assert!(
!caps.allows_read(&link),
"a symlink escaping the granted root must be denied"
);
}
#[test]
fn denies_everything_when_no_capability_is_granted() {
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("f.txt");
fs::write(&file, "x").unwrap();
assert!(!Capabilities::default().allows_read(&file));
}
#[test]
fn denies_a_path_that_does_not_exist() {
let dir = tempfile::tempdir().unwrap();
let caps = Capabilities::new(vec![dir.path().to_path_buf()]);
assert!(!caps.allows_read(&dir.path().join("not-created-yet.txt")));
}
#[test]
fn denies_when_a_granted_root_does_not_exist() {
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("f.txt");
fs::write(&file, "x").unwrap();
let caps = Capabilities::new(vec!["/definitely/not/a/real/path".into()]);
assert!(!caps.allows_read(&file));
}
#[test]
fn a_sibling_directory_sharing_a_name_prefix_is_denied() {
let base = tempfile::tempdir().unwrap();
let granted = base.path().join("data");
let sibling = base.path().join("data-secret");
fs::create_dir(&granted).unwrap();
fs::create_dir(&sibling).unwrap();
let file = sibling.join("f.txt");
fs::write(&file, "nope").unwrap();
let caps = Capabilities::new(vec![granted]);
assert!(
!caps.allows_read(&file),
"a name-prefix sibling must not be treated as nested"
);
}
#[test]
fn any_one_of_several_granted_roots_suffices() {
let a = tempfile::tempdir().unwrap();
let b = tempfile::tempdir().unwrap();
let file = b.path().join("f.txt");
fs::write(&file, "hi").unwrap();
let caps = Capabilities::new(vec![a.path().to_path_buf(), b.path().to_path_buf()]);
assert!(caps.allows_read(&file));
}