use std::path::{Path, PathBuf};
pub mod radix_ulid_sort;
fn path_with_canonical_prefix(path: &Path, prefix: &Path) -> PathBuf {
let Ok(suffix) = path.strip_prefix(prefix) else {
return path.to_path_buf();
};
let Ok(canonical_prefix) = prefix.canonicalize() else {
return path.to_path_buf();
};
canonical_prefix.join(suffix)
}
#[must_use]
pub(crate) fn path_with_canonical_process_temp_prefix(path: &Path) -> PathBuf {
let temp_dir = std::env::temp_dir();
path_with_canonical_prefix(path, &temp_dir)
}
#[cfg(test)]
mod tests {
use super::{path_with_canonical_prefix, path_with_canonical_process_temp_prefix};
use std::path::Path;
#[test]
fn process_temp_child_uses_canonical_temp_prefix() {
let temp_dir = std::env::temp_dir();
let child = temp_dir.join("ee-temp-prefix-test").join("child");
let expected = temp_dir
.canonicalize()
.unwrap_or_else(|_| temp_dir.clone())
.join("ee-temp-prefix-test")
.join("child");
assert_eq!(path_with_canonical_process_temp_prefix(&child), expected);
}
#[test]
fn path_outside_prefix_is_unchanged() {
let path = Path::new("relative-cache/entry.json");
let prefix = std::env::temp_dir();
assert_eq!(path_with_canonical_prefix(path, &prefix), path);
}
#[cfg(unix)]
#[test]
fn canonical_prefix_preserves_symlinked_descendant() {
use std::fs;
use std::os::unix::fs::symlink;
let temp = tempfile::tempdir().expect("create isolated filesystem");
let canonical_prefix = temp.path().join("canonical-prefix");
fs::create_dir(&canonical_prefix).expect("create canonical prefix");
let alias_prefix = temp.path().join("alias-prefix");
symlink(&canonical_prefix, &alias_prefix).expect("create trusted prefix alias");
let outside = temp.path().join("outside");
fs::create_dir(&outside).expect("create outside directory");
fs::write(outside.join("entry.json"), b"outside").expect("create outside entry");
let descendant_link = canonical_prefix.join("descendant-link");
symlink(&outside, &descendant_link).expect("create untrusted descendant symlink");
let input = alias_prefix.join("descendant-link").join("entry.json");
let normalized = path_with_canonical_prefix(&input, &alias_prefix);
let expected = canonical_prefix
.canonicalize()
.expect("canonicalize trusted prefix")
.join("descendant-link")
.join("entry.json");
assert_eq!(normalized, expected);
assert_ne!(
normalized.canonicalize().expect("resolve full input"),
normalized,
"normalization must leave descendant symlinks visible to the safety walker"
);
}
}