use proptest::prelude::*;
use super::*;
use crate::engine::facet::LocalLocus;
use crate::engine::resolve::regions::classify_region;
use crate::pathctx::PathCtx;
use crate::verdict::{SafetyLevel, Verdict};
fn root() -> String {
format!("{}/sc-anchor-probe/projects/app", std::env::var("HOME").unwrap_or_else(|_| "/nonexistent-home".to_string()))
}
fn covered() -> [String; 4] {
let root = root();
let parent = root.trim_end_matches("/app").to_string();
[root.clone(), format!("{root}/web"), format!("{parent}/lib"), "/tmp/x".to_string()]
}
const SPELLINGS: &[&str] = &[
"echo x > {t}", "echo x >> {t}", "tee {t}", "tee -a {t}", "cp a.txt {t}", "mv a.txt {t}", "touch {t}", "mkdir -p {t}",
"sed -i s/a/b/ {t}", "ln -s a.txt {t}", "rm -rf {t}", "sort -o {t} a.txt", "install -m 644 a.txt {t}", "cd {d} && echo x > {t}",
];
fn approved(level: FolderLevel, command: &str) -> bool {
let _ctx =
crate::pathctx::enter(PathCtx { cwd: Some(crate::targets::UNKNOWN_WORKDIR.to_string()), root: Some(root()), session_id: None });
let _folder = enter(level);
crate::cst::command_verdict(command).is_allowed()
}
fn landing(anchor: &str, target: &str) -> LocalLocus {
let _ctx = crate::pathctx::enter(PathCtx { cwd: Some(anchor.to_string()), root: Some(root()), session_id: None });
let resolved = crate::pathctx::resolve(target).into_owned();
if crate::engine::resolve::is_unpinnable(&resolved) {
return LocalLocus::Machine;
}
let role = classify_region(&resolved);
role.write_locus.max(role.rebind_locus)
}
fn segment() -> impl Strategy<Value = String> {
prop_oneof![
8 => "[a-z]{1,6}(\\.[a-z]{1,3})?",
3 => Just("..".to_string()),
1 => Just(".".to_string()),
1 => Just(".git".to_string()),
1 => Just("hooks".to_string()),
1 => Just("pre-commit".to_string()),
1 => Just(".ssh".to_string()),
1 => Just("authorized_keys".to_string()),
1 => Just(".envrc".to_string()),
1 => Just(".zshrc".to_string()),
1 => Just("Library".to_string()),
1 => Just("LaunchAgents".to_string()),
1 => Just("bin".to_string()),
1 => Just(".config".to_string()),
1 => Just("credentials".to_string()),
]
}
fn target() -> impl Strategy<Value = String> {
proptest::collection::vec(segment(), 1..5).prop_map(|s| s.join("/"))
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(1500))]
#[test]
fn an_approved_write_lands_nowhere_protected_in_any_covered_folder(
spelling in proptest::sample::select(SPELLINGS),
t in target(),
d in target(),
level in prop_oneof![Just(FolderLevel::Developer), Just(FolderLevel::Workspace)],
) {
let command = spelling.replace("{t}", &t).replace("{d}", &d);
if !approved(level, &command) {
return Ok(());
}
let target = if spelling.starts_with("cd ") { format!("{d}/{t}") } else { t.clone() };
for anchor in covered() {
let locus = landing(&anchor, &target);
prop_assert!(locus < LocalLocus::WorktreeTrusted, "`{command}` is approved at {} with the folder unknown, and run in {anchor} it writes `{target}` at {locus:?}", level.name());
}
if level == FolderLevel::Developer {
let home = std::env::var("HOME").unwrap_or_default();
let _ctx = crate::pathctx::enter(PathCtx { cwd: Some(home.clone()), root: Some(root()), session_id: None });
let at_home = crate::pathctx::resolve(&target).into_owned();
prop_assert!(!home.is_empty(), "HOME is unset, so the home check cannot run");
prop_assert!(!crate::engine::resolve::regions::names_a_region(&at_home), "`{command}` is approved at developer and run in ~ it writes `{at_home}`, a named place");
}
}
}
#[test]
fn the_soundness_check_sees_a_protected_landing() {
assert!(landing(&root(), ".git/hooks/pre-commit") >= LocalLocus::WorktreeTrusted);
assert!(landing(&root(), "out.txt") < LocalLocus::WorktreeTrusted);
assert!(landing(&format!("{}/web", root()), "../../lib/x") < LocalLocus::WorktreeTrusted);
assert!(!approved(FolderLevel::Workspace, "echo x > .git/hooks/pre-commit"));
}
static ALL_SPELLINGS: std::sync::LazyLock<Vec<&str>> =
std::sync::LazyLock::new(|| SPELLINGS.iter().copied().chain(["cat {t}", "cp {t} b.txt", "grep -r x {t}", "ls {t}"]).collect());
fn at_root(root: &str, command: &str) -> bool {
let _ctx = crate::pathctx::enter(PathCtx { cwd: Some(root.to_string()), root: Some(root.to_string()), session_id: None });
crate::cst::command_verdict(command).is_allowed()
}
fn granted(root: &str, level: FolderLevel, command: &str) -> bool {
let _ctx = crate::pathctx::enter(PathCtx {
cwd: Some(crate::targets::UNKNOWN_WORKDIR.to_string()),
root: Some(root.to_string()),
session_id: None,
});
let _folder = enter(level);
match crate::cst::command_verdict(command) {
Verdict::Allowed(l) => level > FolderLevel::Reads || l <= SafetyLevel::SafeRead,
Verdict::Denied => false,
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(1000))]
#[test]
fn an_unknown_folder_never_approves_what_the_root_refuses(
spelling in proptest::sample::select(ALL_SPELLINGS.clone()),
t in target(),
d in target(),
elsewhere in proptest::bool::ANY,
) {
let command = spelling.replace("{t}", &t).replace("{d}", &d);
let root = if elsewhere { someone_else() } else { root() };
let root_allows = at_root(&root, &command);
let mut stricter: Option<(&str, bool)> = None;
for level in [FolderLevel::Reads, FolderLevel::Developer, FolderLevel::Workspace] {
let allowed = granted(&root, level, &command);
prop_assert!(!allowed || root_allows, "`{command}` is approved at {} with the folder unknown but refused in the project root", level.name());
if let Some((name, was)) = stricter {
prop_assert!(!was || allowed, "`{command}`: approved at {name} but not at {}", level.name());
}
stricter = Some((level.name(), allowed));
}
}
}
#[test]
fn a_path_climbing_out_of_an_unknown_folder_is_placed_nowhere() {
let elsewhere = &someone_else();
for command in ["cat ../x", "cp a ../ b", "cp ../x b", "cat ../../x"] {
assert!(!at_root(elsewhere, command), "precondition: `{command}` is refused in a project under another user's home");
for level in [FolderLevel::Reads, FolderLevel::Developer, FolderLevel::Workspace] {
assert!(!granted(elsewhere, level, command), "`{command}` approved at {}", level.name());
}
}
}
fn someone_else() -> String {
let home = std::env::var("HOME").unwrap_or_else(|_| "/nonexistent-home".to_string());
let users = home.rsplit_once('/').map_or("", |(p, _)| p);
format!("{users}/sc-someone-else/projects/app")
}