commit 09edef0d73fef537d91875be09ac74c6e79e0a14
parent 7dbaa0a5c6534edfec8d582d69e0bfada96cd111
Author: triesap <tyson@radroots.org>
Date: Thu, 6 Aug 2026 07:32:52 +0000
mobile: define windows logging acl and reparse behavior
- fail closed before any file mutation when Windows ACL enforcement is unavailable
- prohibit file logging until reparse-point checks are active
- retain stdout logging as the supported secure Windows capability
- cover the capability boundary with a Windows-targeted regression
Diffstat:
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/core/crates/tera_ffi/src/logging.rs b/core/crates/tera_ffi/src/logging.rs
@@ -116,6 +116,13 @@ type FileWriter = tracing_appender::non_blocking::NonBlocking;
fn build_file_writer(
options: &LoggingOptions,
) -> Result<(Option<FileWriter>, Option<WorkerGuard>), String> {
+ #[cfg(windows)]
+ if options.dir.is_some() {
+ return Err(
+ "secure file logging is unavailable until Windows ACL and reparse-point enforcement is active"
+ .to_owned(),
+ );
+ }
let Some(dir) = options.dir.as_ref() else {
return Ok((None, None));
};
@@ -247,6 +254,22 @@ mod tests {
log_debug("debug".to_owned()).expect("debug");
}
+ #[cfg(windows)]
+ #[test]
+ fn windows_file_logging_fails_before_filesystem_mutation() {
+ let directory = tempfile::tempdir().expect("temporary directory");
+ let requested = directory.path().join("must-not-exist");
+ let options = LoggingOptions {
+ dir: Some(requested.clone()),
+ stdout: false,
+ ..LoggingOptions::default()
+ };
+ let error = build_file_writer(&options).expect_err("ACL capability must fail closed");
+ assert!(error.contains("ACL"));
+ assert!(error.contains("reparse-point"));
+ assert!(!requested.exists());
+ }
+
#[test]
fn initialization_is_idempotent_but_rejects_reconfiguration() {
let directory = tempfile::tempdir().expect("temporary directory");
diff --git a/core/crates/tera_ffi/src/logging/writer.rs b/core/crates/tera_ffi/src/logging/writer.rs
@@ -159,7 +159,7 @@ impl Write for SizeRotatingWriter {
}
}
-#[cfg(any(test, not(unix)))]
+#[cfg(test)]
fn reject_unsafe_target(path: &Path) -> io::Result<()> {
match fs::symlink_metadata(path) {
Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_file() => Err(
@@ -230,7 +230,7 @@ fn open_append(_path: &Path) -> io::Result<File> {
))
}
-#[cfg(any(test, not(unix)))]
+#[cfg(test)]
fn rotated_path(path: &Path, index: usize) -> PathBuf {
let mut value = path.as_os_str().to_owned();
value.push(format!(".{index}"));