diff --git a/src-tauri/src/cloud_review.rs b/src-tauri/src/cloud_review.rs index 349a75ee1..15ea9db7b 100644 --- a/src-tauri/src/cloud_review.rs +++ b/src-tauri/src/cloud_review.rs @@ -279,6 +279,13 @@ fn secure_decision_directory(path: &Path) -> Result<(), String> { if !metadata.is_dir() || metadata.file_type().is_symlink() { return Err("cloud-review-directory-unsafe".into()); } + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + if metadata.permissions().mode() & 0o022 != 0 { + return Err("cloud-review-directory-writable-by-others".into()); + } + } Ok(()) } diff --git a/src-tauri/tests/cloud_review_directory_authority.rs b/src-tauri/tests/cloud_review_directory_authority.rs new file mode 100644 index 000000000..b46ab7719 --- /dev/null +++ b/src-tauri/tests/cloud_review_directory_authority.rs @@ -0,0 +1,19 @@ +#[cfg(unix)] +#[test] +fn shared_writable_cloud_review_directory_fails_closed() { + use disksage_lib::cloud_review::load_latest_decisions; + use std::os::unix::fs::PermissionsExt; + + let directory = tempfile::tempdir().expect("temporary review directory"); + let mut permissions = std::fs::metadata(directory.path()) + .expect("review directory metadata") + .permissions(); + permissions.set_mode(0o777); + std::fs::set_permissions(directory.path(), permissions) + .expect("make review directory shared-writable for regression"); + + let error = load_latest_decisions(directory.path()) + .expect_err("shared-writable review authority must fail closed"); + + assert_eq!(error, "cloud-review-directory-writable-by-others"); +}