Skip to main content

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 canonical_root = root
9        .canonicalize()
10        .with_context(|| format!("failed to canonicalize root {}", root.display()))?;
11
12    let resolved = if candidate.is_absolute() {
13        candidate.to_path_buf()
14    } else {
15        canonical_root.join(candidate)
16    };
17
18    let canon = resolved
19        .canonicalize()
20        .with_context(|| format!("failed to canonicalize {}", resolved.display()))?;
21
22    if !canon.starts_with(&canonical_root) {
23        anyhow::bail!(
24            "path escapes root ({}): {}",
25            canonical_root.display(),
26            canon.display()
27        );
28    }
29
30    Ok(canon)
31}