Coverage Report

Created: 2025-07-11 06:22

/rust/registry/src/index.crates.io-6f17d22bba15001f/cap-primitives-3.4.4/src/fs/canonicalize.rs
Line
Count
Source (jump to first uncovered line)
1
//! Sandboxed path canonicalization.
2
3
use crate::fs::canonicalize_impl;
4
#[cfg(racy_asserts)]
5
use crate::fs::{file_path, open, OpenOptions};
6
use std::path::{Path, PathBuf};
7
use std::{fs, io};
8
9
/// Canonicalize the given path, ensuring that the resolution of the path never
10
/// escapes the directory tree rooted at `start`.
11
#[cfg_attr(not(racy_asserts), allow(clippy::let_and_return))]
12
#[inline]
13
0
pub fn canonicalize(start: &fs::File, path: &Path) -> io::Result<PathBuf> {
14
0
    // Call the underlying implementation.
15
0
    let result = canonicalize_impl(start, path);
16
0
17
0
    #[cfg(racy_asserts)]
18
0
    check_canonicalize(start, path, &result);
19
0
20
0
    result
21
0
}
22
23
#[cfg(racy_asserts)]
24
fn check_canonicalize(start: &fs::File, path: &Path, result: &io::Result<PathBuf>) {
25
    if let Ok(canonical_path) = result {
26
        let path_result = open(start, path, OpenOptions::new().read(true));
27
        let canonical_result = open(start, canonical_path, OpenOptions::new().read(true));
28
        match (path_result, canonical_result) {
29
            (Ok(path_file), Ok(canonical_file)) => assert_same_file!(
30
                &path_file,
31
                &canonical_file,
32
                "we should be able to stat paths that we just canonicalized"
33
            ),
34
            (Err(path_err), Err(canonical_err)) => {
35
                assert_eq!(path_err.to_string(), canonical_err.to_string())
36
            }
37
            other => {
38
                // TODO: Checking in the case it does end with ".".
39
                if !path.to_string_lossy().ends_with("/.") {
40
                    panic!("inconsistent canonicalize checks: {:?}", other);
41
                }
42
            }
43
        }
44
45
        // On operating systems which can tell us the path of a file descriptor,
46
        // assert that the path we computed canonicalizes to the same thing as
47
        // the input canonicalizes too.
48
        if let Some(start_abspath) = file_path(start) {
49
            let check_abspath = start_abspath.join(path);
50
            let result_abspath = start_abspath.join(canonical_path);
51
            if let Ok(check_abspath) = fs::canonicalize(check_abspath) {
52
                let result_abspath =
53
                    fs::canonicalize(result_abspath).expect("we already canonicalized this");
54
                assert_eq!(
55
                    check_abspath,
56
                    result_abspath,
57
                    "incorrect canonicalization: start='{:?}' path='{}' result='{}'",
58
                    start,
59
                    path.display(),
60
                    canonical_path.display()
61
                );
62
                // TODO: When porting to Windows, check whether `start_abspath` not being
63
                // a canonicalized path leads to `\\?\` extended path prefix differences.
64
                assert!(
65
                    result_abspath.starts_with(start_abspath),
66
                    "sandbox escape: start='{:?}' path='{}' result='{}'",
67
                    start,
68
                    path.display(),
69
                    canonical_path.display()
70
                );
71
            }
72
        }
73
    }
74
}