<!--
title: Secure path resolution
domain: security
topic: Security
language: Rust
source: can1357/oh-my-pi
updated: 2026-10-21
url: https://awesomereviewers.com/reviewers/oh-my-pi-secure-path-resolution/
-->

When validating or resolving filesystem paths that cross trust boundaries (e.g., clone/checkout destinations, or pointer files copied from another repository), never trust lexical or unresolved paths — they can be disguised via symlinks or `..` traversal to escape intended boundaries or to keep referencing the original source's data after a copy. Canonicalize only the path components that already exist (to correctly follow symlinked ancestors) and treat the final, possibly not-yet-created component literally, since a symlink attack most often targets that last segment. Explicitly reject destinations that resolve inside a protected source tree, and detect/rewrite any embedded references (such as `.git` pointer files) that still resolve into the source's internal metadata after being copied, so the new location cannot silently share or leak state with the source. For example, resolve a destination path by canonicalizing each existing ancestor and only lexically applying the remaining components, then compare the fully resolved path against the forbidden source root before proceeding:

```rust
fn resolve_destination(path: &Path) -> PathBuf {
    let absolute = std::path::absolute(path).unwrap_or_else(|_| path.to_path_buf());
    let mut resolved = PathBuf::new();
    for component in absolute.components() {
        match component {
            Component::ParentDir => { resolved.pop(); },
            Component::Normal(name) => {
                let candidate = resolved.join(name);
                // Only existing components are canonicalized; the final,
                // possibly non-existent entry stays lexical so a symlink
                // there cannot be used to disguise the real destination.
                resolved = fs::canonicalize(&candidate).unwrap_or(candidate);
            },
            _ => {}
        }
    }
    resolved
}

if resolve_destination(dest).starts_with(source_root) {
    return Err(Error::backend("clone", "destination inside source"));
}
```
