runner_core/
path_safety.rs1use std::path::{Path, PathBuf};
2
3use anyhow::{Context, Result};
4
5pub fn normalize_under_root(root: &Path, candidate: &Path) -> Result<PathBuf> {
8 if candidate.is_absolute() {
9 anyhow::bail!("absolute paths are not allowed: {}", candidate.display());
10 }
11
12 let joined = root.join(candidate);
13 let canon = joined
14 .canonicalize()
15 .with_context(|| format!("failed to canonicalize {}", joined.display()))?;
16
17 if !canon.starts_with(root) {
18 anyhow::bail!(
19 "path escapes root ({}): {}",
20 root.display(),
21 canon.display()
22 );
23 }
24
25 Ok(canon)
26}