use std::path::Path;
pub fn harden_owner_only(path: &Path) {
#[cfg(target_os = "windows")]
harden_windows_acl(path);
#[cfg(not(target_os = "windows"))]
let _ = path;
}
#[cfg(target_os = "windows")]
fn harden_windows_acl(path: &Path) {
use std::process::Command;
let path_str = match path.to_str() {
Some(s) => s,
None => {
tracing::warn!(?path, "ACL hardening skipped: path is not valid UTF-8");
return;
}
};
let run_icacls = |args: &[&str]| -> std::io::Result<()> {
let status = Command::new("icacls").args(args).status()?;
if status.success() {
Ok(())
} else {
Err(std::io::Error::other(format!(
"icacls {args:?} exited with status {status}"
)))
}
};
let _ = run_icacls(&[path_str, "/grant:r", "*S-1-5-18:F"]);
if let Some(u) = std::env::var_os("USERNAME").and_then(|v| v.into_string().ok()) {
let grant = format!("{u}:F");
let _ = run_icacls(&[path_str, "/grant:r", &grant]);
}
if let Err(e) = run_icacls(&[path_str, "/inheritance:r"]) {
tracing::warn!(?e, ?path, "ACL hardening: /inheritance:r failed");
}
let _ = run_icacls(&[path_str, "/remove:g", "*S-1-5-32-544"]);
let _ = run_icacls(&[path_str, "/remove:d", "*S-1-5-32-544"]);
if let Some(u) = std::env::var_os("USERNAME").and_then(|v| v.into_string().ok()) {
if let Err(e) = run_icacls(&[path_str, "/setowner", &u]) {
tracing::warn!(?e, ?path, "ACL hardening: /setowner failed");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn harden_is_a_noop_on_a_missing_path_and_never_panics() {
harden_owner_only(Path::new("this/path/does/not/exist/xyz"));
}
}