use std::path::Path;
pub fn restrict_file_to_owner(path: &Path) -> std::io::Result<()> {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perm = std::fs::metadata(path)?.permissions();
perm.set_mode(0o600);
std::fs::set_permissions(path, perm)?;
}
#[cfg(windows)]
{
apply_windows_user_only_dacl(path)?;
}
#[cfg(not(any(unix, windows)))]
{
let _ = path;
}
Ok(())
}
pub fn restrict_dir_to_owner(path: &Path) -> std::io::Result<()> {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perm = std::fs::metadata(path)?.permissions();
perm.set_mode(0o700);
std::fs::set_permissions(path, perm)?;
}
#[cfg(windows)]
{
apply_windows_user_only_dacl(path)?;
}
#[cfg(not(any(unix, windows)))]
{
let _ = path;
}
Ok(())
}
#[cfg(windows)]
fn apply_windows_user_only_dacl(path: &Path) -> std::io::Result<()> {
use std::process::Command;
let user = std::env::var("USERNAME").map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::NotFound,
"USERNAME env var not set — cannot apply Windows user-only DACL",
)
})?;
let user_trimmed = user.trim();
if user_trimmed.is_empty() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"USERNAME is empty — cannot apply Windows user-only DACL",
));
}
let path_str = path.to_str().ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"path is not valid UTF-8 — cannot pass to icacls",
)
})?;
let output = Command::new("icacls")
.arg(path_str)
.arg("/inheritance:r")
.arg("/grant:r")
.arg(format!("{user_trimmed}:(F)"))
.output()?;
if !output.status.success() {
return Err(std::io::Error::other(format!(
"icacls failed ({}): {}",
output.status,
String::from_utf8_lossy(&output.stderr).trim()
)));
}
Ok(())
}
#[cfg(all(test, unix))]
mod tests {
use super::*;
#[test]
fn restrict_file_sets_0600_on_unix() {
use std::os::unix::fs::PermissionsExt;
let tmp = std::env::temp_dir().join(format!("vta-test-secure-{}", rand::random::<u32>()));
std::fs::create_dir_all(&tmp).unwrap();
let f = tmp.join("secret.bin");
std::fs::write(&f, b"sensitive").unwrap();
let mut perm = std::fs::metadata(&f).unwrap().permissions();
perm.set_mode(0o644);
std::fs::set_permissions(&f, perm).unwrap();
restrict_file_to_owner(&f).expect("restrict_file_to_owner succeeds");
let mode = std::fs::metadata(&f).unwrap().permissions().mode();
assert_eq!(mode & 0o777, 0o600);
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn restrict_dir_sets_0700_on_unix() {
use std::os::unix::fs::PermissionsExt;
let tmp = std::env::temp_dir().join(format!("vta-test-secure-{}", rand::random::<u32>()));
std::fs::create_dir_all(&tmp).unwrap();
let mut perm = std::fs::metadata(&tmp).unwrap().permissions();
perm.set_mode(0o755);
std::fs::set_permissions(&tmp, perm).unwrap();
restrict_dir_to_owner(&tmp).expect("restrict_dir_to_owner succeeds");
let mode = std::fs::metadata(&tmp).unwrap().permissions().mode();
assert_eq!(mode & 0o777, 0o700);
let _ = std::fs::remove_dir_all(&tmp);
}
}