Repository: DioxusLabs/dioxus
File: packages/manganis/manganis-macro/src/asset.rs (Rust)

Code before:
    // is the root of
    // /users/dioxus/dev/app/assets/blah.css
    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
        .map(PathBuf::from)
        .map(std::path::absolute)
        .unwrap()
        .unwrap();

    // 1. the input file should be a pathbuf
    let input = PathBuf::from(raw);

    // 2. absolute path to the asset
    let path =
        std::path::absolute(manifest_dir.join(raw.trim_start_matches('/'))).map_err(|err| {
            AssetParseError::AssetDoesntExist {
                err,
                path: input.clone(),
            }
        })?;

    // 3. Ensure the path is not the current dir or exist outside the current dir
    if path == manifest_dir || !path.starts_with(manifest_dir) || !path.exists() {
        return Err(AssetParseError::InvalidPath { path });
    }

    Ok(path)
}

fn hash_file_contents(file_path: &Path) -> Result<u64, AssetParseError> {
    // Create a hasher
    let mut hash = std::collections::hash_map::DefaultHasher::new();

    // If this is a folder, hash the folder contents
    if file_path.is_dir() {
        let files =
            std::fs::read_dir(file_path).map_err(|err| AssetParseError::AssetDoesntExist {
                err,
                path: file_path.to_path_buf(),
            })?;
        for file in files.flatten() {
            let path = file.path();
            hash_file_contents(&path)?;
        }
        return Ok(hash.finish());
    }

    // Otherwise, open the file to get its contents
    let mut file =
        std::fs::File::open(file_path).map_err(|err| AssetParseError::AssetDoesntExist {
            err,
            path: file_path.to_path_buf(),

Review comment (bug):
Instead, this case triggers which gives the wrong error message

Code after:
        .unwrap();

    // 1. the input file should be a pathbuf
    let input = PathBuf::from(raw);

    // 2. absolute path to the asset
    let Ok(path) = std::path::absolute(manifest_dir.join(raw.trim_start_matches('/'))) else {
        return Err(AssetParseError::InvalidPath {
            path: input.clone(),
        });
    };

    // 3. Ensure the path exists
    let Ok(path) = path.canonicalize() else {
        return Err(AssetParseError::AssetDoesntExist {
            path: input.clone(),
        });
    };

    // 4. Ensure the path doesn't escape the crate dir
    //
    // - Note: since we called canonicalize on both paths, we can safely compare the parent dirs.
    //   On windows, we can only compare the prefix if both paths are canonicalized (not just absolute)
    //   https://github.com/rust-lang/rust/issues/42869
    if path == manifest_dir || !path.starts_with(manifest_dir) {
        return Err(AssetParseError::InvalidPath { path });
    }

    Ok(path)
}

fn hash_file_contents(file_path: &Path) -> Result<u64, AssetParseError> {
    // Create a hasher
    let mut hash = std::collections::hash_map::DefaultHasher::new();

    // If this is a folder, hash the folder contents
    if file_path.is_dir() {
        let files = std::fs::read_dir(file_path).map_err(|err| AssetParseError::IoError {
            err,
            path: file_path.to_path_buf(),
        })?;
        for file in files.flatten() {
            let path = file.path();
            hash_file_contents(&path)?;
        }
        return Ok(hash.finish());
    }

    // Otherwise, open the file to get its contents
    let mut file = std::fs::File::open(file_path).map_err(|err| AssetParseError::IoError {
        err,