//! Integration test for the dangling-symlink write-escape fix. //! //! Runs against the library's public API so it compiles/executes independently //! of unrelated in-crate test fixtures. Reproduces the concrete escape an //! approved `ln -s` could otherwise open: a symlink whose target does not exist //! yet (so `exists()` is false and `canonicalize()` fails) must still be //! rejected when it points outside the per-session sandbox, instead of being //! treated as a brand-new in-sandbox path that a Write would follow out. #![cfg(unix)] use boosapi::services::agent_workbench::sandbox::{check_tool_paths, validate_path_within_cwd}; use serde_json::json; use std::os::unix::fs::symlink; use std::path::PathBuf; fn tmp_sandbox() -> PathBuf { let base = std::env::temp_dir().join(format!("agent_it_sb_{}", uuid::Uuid::new_v4().simple())); std::fs::create_dir_all(&base).unwrap(); base.canonicalize().unwrap() } #[test] fn dangling_symlink_to_absolute_target_is_rejected() { let sb = tmp_sandbox(); symlink("/etc/cron.d/pwn_it", sb.join("evil")).unwrap(); let res = validate_path_within_cwd("evil", &sb); assert!(res.is_err(), "expected rejection, got {res:?}"); } #[test] fn dangling_relative_symlink_escape_is_rejected() { let sb = tmp_sandbox(); symlink("../../escape_it", sb.join("evil")).unwrap(); assert!(validate_path_within_cwd("evil", &sb).is_err()); } #[test] fn write_tool_through_dangling_symlink_is_blocked() { let sb = tmp_sandbox(); symlink("/etc/cron.d/pwn_it2", sb.join("evil")).unwrap(); let input = json!({ "path": "evil", "content": "* * * * * root sh -c id" }); assert!(check_tool_paths("write", &input, &sb).is_err()); } #[test] fn write_through_intermediate_dangling_symlink_dir_is_blocked() { let sb = tmp_sandbox(); symlink("/etc/nonexistent_it_dir", sb.join("evildir")).unwrap(); let input = json!({ "path": "evildir/pwn", "content": "x" }); assert!(check_tool_paths("write", &input, &sb).is_err()); } #[test] fn dangling_symlink_pointing_inside_sandbox_is_allowed() { let sb = tmp_sandbox(); symlink(sb.join("future_file"), sb.join("ok_link")).unwrap(); assert!(validate_path_within_cwd("ok_link", &sb).is_ok()); } #[test] fn ordinary_new_file_still_allowed() { let sb = tmp_sandbox(); assert!(validate_path_within_cwd("notes/today.md", &sb).is_ok()); }