|
| 1 | +use super::*; |
| 2 | +fn scope(root: &Path) -> Result<std::sync::Arc<PathScope>, tinysecurity_bus::Error> { |
| 3 | + let mut registry = crate::PathPolicyRegistry::default(); |
| 4 | + let policy = PathPolicy { |
| 5 | + enabled: false, |
| 6 | + workspace_only: false, |
| 7 | + workspace_dir: root.into(), |
| 8 | + action_dir: root.into(), |
| 9 | + home_dir: None, |
| 10 | + forbidden_paths: vec![], |
| 11 | + trusted_roots: vec![], |
| 12 | + reserved_paths: vec![], |
| 13 | + reserved_prefixes: vec![], |
| 14 | + readonly_paths: vec![], |
| 15 | + reserved_names: vec![], |
| 16 | + }; |
| 17 | + let id = registry.register(policy)?; |
| 18 | + registry.get_owned(&id) |
| 19 | +} |
| 20 | +/// Native critical-system targets fail before any filesystem read or write. |
| 21 | +#[test] |
| 22 | +fn platform_native_protected_target_is_denied_before_resolution() |
| 23 | +-> Result<(), Box<dyn std::error::Error>> { |
| 24 | + let root = tempfile::tempdir()?; |
| 25 | + #[cfg(windows)] |
| 26 | + let native = PathBuf::from( |
| 27 | + std::env::var_os("SystemRoot") |
| 28 | + .ok_or_else(|| std::io::Error::other("Windows SystemRoot is absent"))?, |
| 29 | + ) |
| 30 | + .join("System32"); |
| 31 | + #[cfg(not(windows))] |
| 32 | + let native = PathBuf::from("/etc"); |
| 33 | + assert!(native.is_absolute()); |
| 34 | + assert!(protected_path(&native)); |
| 35 | + let scope = scope(root.path())?; |
| 36 | + for write in [false, true] { |
| 37 | + assert_eq!( |
| 38 | + scope.validate(&native.to_string_lossy(), write), |
| 39 | + PathValidationResult::Denied { |
| 40 | + category: Rejection::Protected |
| 41 | + } |
| 42 | + ); |
| 43 | + } |
| 44 | + Ok(()) |
| 45 | +} |
| 46 | +/// Windows-looking namespace prefixes do not change native POSIX path meaning. |
| 47 | +#[cfg(unix)] |
| 48 | +#[test] |
| 49 | +fn posix_question_mark_directory_is_not_a_windows_root_or_action_relative_path() { |
| 50 | + use std::path::Component; |
| 51 | + let path = Path::new("//?/etc/passwd"); |
| 52 | + assert!(path.is_absolute()); |
| 53 | + let components: Vec<_> = path.components().collect(); |
| 54 | + assert_eq!( |
| 55 | + components, |
| 56 | + vec![ |
| 57 | + Component::RootDir, |
| 58 | + Component::Normal("?".as_ref()), |
| 59 | + Component::Normal("etc".as_ref()), |
| 60 | + Component::Normal("passwd".as_ref()) |
| 61 | + ] |
| 62 | + ); |
| 63 | + // The normalized spelling is used for floor classification only. It is |
| 64 | + // never used to construct the filesystem target passed to canonicalize. |
| 65 | + assert!(!protected_path(path)); |
| 66 | + let root = Path::new("/acting-root"); |
| 67 | + let policy = PathPolicy { |
| 68 | + enabled: false, |
| 69 | + workspace_only: false, |
| 70 | + workspace_dir: root.into(), |
| 71 | + action_dir: root.into(), |
| 72 | + home_dir: None, |
| 73 | + forbidden_paths: vec![], |
| 74 | + trusted_roots: vec![], |
| 75 | + reserved_paths: vec![], |
| 76 | + reserved_prefixes: vec![], |
| 77 | + readonly_paths: vec![], |
| 78 | + reserved_names: vec![], |
| 79 | + }; |
| 80 | + let scope = PathScope::registered(policy); |
| 81 | + assert_eq!( |
| 82 | + scope.absolute("//?/etc/passwd"), |
| 83 | + PathBuf::from("//?/etc/passwd") |
| 84 | + ); |
| 85 | + assert!(protected_path(Path::new("/etc/passwd"))); |
| 86 | +} |
0 commit comments