fix: validate linked worktree trust metadata

This commit is contained in:
Eva Wong
2026-06-08 12:58:30 -07:00
parent 9e0d7f02c9
commit 2afa2b0c33
2 changed files with 59 additions and 0 deletions

View File

@@ -615,6 +615,11 @@ async fn resolve_root_git_project_for_trust_detects_worktree_pointer_without_git
format!("gitdir: {}\n", worktree_git_dir.display()),
)
.unwrap();
std::fs::write(
worktree_git_dir.join("gitdir"),
format!("{}\n", worktree_root.join(".git").display()),
)
.unwrap();
let expected = repo_root.abs();
let worktree_root = worktree_root.abs();
@@ -629,6 +634,30 @@ async fn resolve_root_git_project_for_trust_detects_worktree_pointer_without_git
);
}
#[tokio::test]
async fn resolve_root_git_project_for_trust_rejects_forged_worktree_pointer() {
let tmp = TempDir::new().expect("tempdir");
let trusted_repo = tmp.path().join("trusted");
let forged_repo = tmp.path().join("forged");
let forged_git_dir = trusted_repo
.join(".git")
.join("worktrees")
.join("feature-x");
std::fs::create_dir_all(&forged_git_dir).unwrap();
std::fs::create_dir_all(&forged_repo).unwrap();
std::fs::write(
forged_repo.join(".git"),
format!("gitdir: {}\n", forged_git_dir.display()),
)
.unwrap();
assert!(
resolve_root_git_project_for_trust(LOCAL_FS.as_ref(), &forged_repo.abs())
.await
.is_none()
);
}
#[tokio::test]
async fn resolve_root_git_project_for_trust_non_worktrees_gitdir_returns_none() {
let tmp = TempDir::new().expect("tempdir");

View File

@@ -764,11 +764,41 @@ pub async fn resolve_root_git_project_for_trust(
}
let git_dir_path = AbsolutePathBuf::resolve_path_against_base(git_dir_rel, repo_root.as_path());
if !fs
.get_metadata(&git_dir_path, /*sandbox*/ None)
.await
.ok()?
.is_directory
{
return None;
}
let worktrees_dir = git_dir_path.parent()?;
if worktrees_dir.as_path().file_name() != Some(OsStr::new("worktrees")) {
return None;
}
// A linked worktree is valid only when its gitdir points back to this checkout's .git file.
let git_dir_backref = fs
.read_file_text(&git_dir_path.join("gitdir"), /*sandbox*/ None)
.await
.ok()?;
let git_dir_backref = git_dir_backref.trim();
if git_dir_backref.is_empty() {
return None;
}
let git_dir_backref_path =
AbsolutePathBuf::resolve_path_against_base(git_dir_backref, git_dir_path.as_path());
if fs
.canonicalize(&git_dir_backref_path, /*sandbox*/ None)
.await
.ok()?
!= fs.canonicalize(&dot_git, /*sandbox*/ None).await.ok()?
{
return None;
}
let common_dir = worktrees_dir.parent()?;
common_dir.parent()
}