greentic_dev/
path_safety.rs

1use std::path::{Path, PathBuf};
2
3use anyhow::{Context, Result};
4
5/// Normalize a user-supplied path and ensure it stays within an allowed root.
6/// Reject absolute paths and any that escape via `..`.
7pub fn normalize_under_root(root: &Path, candidate: &Path) -> Result<PathBuf> {
8    let resolved = if candidate.is_absolute() {
9        candidate.to_path_buf()
10    } else {
11        root.join(candidate)
12    };
13
14    let canon = resolved
15        .canonicalize()
16        .with_context(|| format!("failed to canonicalize {}", resolved.display()))?;
17
18    if !canon.starts_with(root) {
19        anyhow::bail!(
20            "path escapes root ({}): {}",
21            root.display(),
22            canon.display()
23        );
24    }
25
26    Ok(canon)
27}