diff --git a/crates/fs-operation/src/glob.rs b/crates/fs-operation/src/glob.rs index fe5791a3e..2ade239b6 100644 --- a/crates/fs-operation/src/glob.rs +++ b/crates/fs-operation/src/glob.rs @@ -47,6 +47,15 @@ pub fn run_glob( let mut walker = WalkBuilder::new(walker_root); walker.hidden(false).follow_links(false); if traversal.is_some() { + // Descriptor providers must never let WalkBuilder open pathname-based + // ignore files, which may be symlinked outside the pinned root. Glob's + // explicit pattern remains the complete selection contract. + walker + .git_ignore(false) + .git_global(false) + .git_exclude(false) + .ignore(false) + .parents(false); let search_relative = base.strip_prefix(root).map_err(|_| { FsError::InvalidArgument("glob base is outside its provider root".to_string()) })?; diff --git a/crates/workdir/src/external_local_tests.rs b/crates/workdir/src/external_local_tests.rs index 3fe822993..cf4694f7a 100644 --- a/crates/workdir/src/external_local_tests.rs +++ b/crates/workdir/src/external_local_tests.rs @@ -290,6 +290,22 @@ async fn external_grep_honors_gitignore_above_a_nested_search_root() { root.path().join(".gitignore"), ) .unwrap(); + let glob_with_symlinked_ignore = WorkdirSession::glob( + &session, + GlobRequest { + pattern: "*.txt".to_string(), + path: WorkdirPath::new("").unwrap(), + limit: 10, + }, + ) + .await + .unwrap(); + assert!( + glob_with_symlinked_ignore + .paths + .iter() + .any(|path| path.as_str() == "nested/visible.txt") + ); let symlink_error = WorkdirSession::grep( &session, GrepRequest {