Repository: gitbutlerapp/gitbutler
File: crates/gitbutler-repo/src/commands.rs (Rust)

Code before:
            bail!(
                "Refusing to read '{relative_path:?}' from commit {commit_id:?} as it's not relative to the worktree"
            );
        }

        let repo = self.git2_repo.get()?;
        let tree = repo.find_commit(commit_id)?.tree()?;

        Ok(match tree.get_path(relative_path) {
            Ok(entry) => {
                let blob = repo.find_blob(entry.id())?;
                FileInfo::from_content(relative_path, blob.content())
            }
            Err(e) if e.code() == git2::ErrorCode::NotFound => FileInfo::deleted(),
            Err(e) => return Err(e.into()),
        })
    }

    fn read_file_from_workspace(&self, probably_relative_path: &Path) -> Result<FileInfo> {
        let workdir = self.workdir_or_fail()?;
        let canonical_workdir = gix::path::realpath(&workdir)?;
        let repo = self.git2_repo.get()?;
        let (path_in_worktree, relative_path) = if probably_relative_path.is_relative() {
            (
                // This keeps non-existing leaf components, which preserves deleted-file fallback
                // behavior below.
                gix::path::realpath(workdir.join(probably_relative_path))?,
                probably_relative_path.to_owned(),
            )
        } else {
            let path_in_worktree = gix::path::realpath(probably_relative_path)?;
            let relative_path = match path_in_worktree.strip_prefix(&canonical_workdir) {
                Ok(relative_path) => relative_path.to_owned(),
                Err(_) => {
                    bail!(
                        "Path to read from at '{}' isn't in the worktree directory '{}'",
                        probably_relative_path.display(),
                        canonical_workdir.display()
                    );
                }
            };
            (path_in_worktree, relative_path)
        };

        if !path_in_worktree.starts_with(&canonical_workdir) {
            bail!(
                "Refusing to read path '{}' as it resolves outside the worktree directory '{}'",
                probably_relative_path.display(),
                canonical_workdir.display()
            );
        }

Review comment (suggestion):
I am wondering why we'd fiddle with the `probably_relative_path` so much as it should always be relative. 
Probably one could run `gix::path::normalize()` on it to remove possible attempts to do `../` and if it's not the same afterwards, someone tries to deceive you.

If we use `workdir.join()` it will possibly override the workdir if it's absolute, so we'd have to strip the workdir afterward to be sure. But it might be enough to check with is_relative() because it should always be relative.

Maybe also document what `probably_relative_path` is supposed to be - to my mind it should only ever be a path relative to the repository as it would be in the `.git/index`, and everything else the above *should* catch.

Code after:
    }

    fn read_file_from_commit(&self, commit_id: Oid, relative_path: &Path) -> Result<FileInfo> {
        if !relative_path.is_relative() {
            bail!(
                "Refusing to read '{relative_path:?}' from commit {commit_id:?} as it's not relative to the worktree"
            );
        }

        let repo = self.git2_repo.get()?;
        let tree = repo.find_commit(commit_id)?.tree()?;

        Ok(match tree.get_path(relative_path) {
            Ok(entry) => {
                let blob = repo.find_blob(entry.id())?;
                FileInfo::from_content(relative_path, blob.content())
            }
            Err(e) if e.code() == git2::ErrorCode::NotFound => FileInfo::deleted(),
            Err(e) => return Err(e.into()),
        })
    }

    /// Note that `path` can be relative or absolute, and we must validate that it's in the worktree.
    fn read_file_from_workspace(&self, path: &Path) -> Result<FileInfo> {
        let workdir = self.workdir_or_fail()?;
        let canonical_workdir = gix::path::realpath(&workdir)?;
        let path = gix::path::realpath(canonical_workdir.join(path))?;
        // Double-check that the path is still in the worktree - this might not be the case
        // if it was aboslute to begin with, or leads through symlinks.
        let relative_path = match path.strip_prefix(&canonical_workdir) {
            Ok(relative_path) => relative_path.to_owned(),
            Err(_) => {
                bail!(
                    "Path to read from at '{}' isn't in the worktree directory '{}'",
                    path.display(),
                    canonical_workdir.display()
                );
            }
        };

        let out = match path.symlink_metadata() {
            Ok(md) => {
                if md.is_file() {
                    let content = std::fs::read(&path)?;
                    FileInfo::from_content(&relative_path, &content)
                } else if md.is_symlink() {
                    let content = std::fs::read_link(&path)?;
                    FileInfo::utf8_text_or_binary(&relative_path, &gix::path::into_bstr(content))
                } else if md.is_dir() {
                    bail!(
                        "Path to read from at '{}' is a directory",