From d9ec77a1643c6519c20a8f6e83aab9a3bbef91be Mon Sep 17 00:00:00 2001 From: "Gang Wang (BEYONDSOFT CONSULTING INC)" Date: Fri, 29 May 2026 16:47:07 +0800 Subject: [PATCH 1/2] Improve Symlink cycle condition --- src/Framework.UnitTests/FileMatcher_Tests.cs | 44 ++++++++++++++++++++ src/Framework/Utilities/FileMatcher.cs | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/Framework.UnitTests/FileMatcher_Tests.cs b/src/Framework.UnitTests/FileMatcher_Tests.cs index 9992e52ed85..0cf249aaa63 100644 --- a/src/Framework.UnitTests/FileMatcher_Tests.cs +++ b/src/Framework.UnitTests/FileMatcher_Tests.cs @@ -91,6 +91,50 @@ public void DoNotFollowRecursiveSymlinks() } } } + + [RequiresSymbolicLinksFact] + public void ShouldNotSkipSymlinkDirectoryWhenProjectDirectoryIsPrefixedWithSymlinkTargetName() + { + TransientTestFolder rootFolder = _env.CreateFolder(); + + string targetFolderName = "target"; + string fileAName = "A.cs"; + string targetSubFolderName = "foo"; + string fileBName = "B.cs"; + TransientTestFolder targetFolder = _env.CreateFolder(Path.Combine(rootFolder.Path, targetFolderName)); + _env.CreateFile(targetFolder, fileName: fileAName); + TransientTestFolder targetSubFolder = _env.CreateFolder(Path.Combine(targetFolder.Path, targetSubFolderName)); + _env.CreateFile(targetSubFolder, fileName: fileBName); + + TransientTestFolder projectFolder = _env.CreateFolder(Path.Combine(rootFolder.Path, $"{targetFolderName}Test")); + string symlinkName = "mySymlink"; + string symlinkPath = Path.Combine(projectFolder.Path, symlinkName); + string include = Path.Combine(symlinkName, "**", "*.cs"); + + string[] expectedFiles = + [ + Path.Combine(symlinkName, fileAName), + Path.Combine(symlinkName, targetSubFolderName, fileBName) + ]; + + try + { + Directory.CreateSymbolicLink(symlinkPath, targetFolder.Path); + string[] fileMatches = FileMatcher.Default.GetFiles(projectFolder.Path, include).FileList; + fileMatches.Length.ShouldBe(expectedFiles.Length); + foreach (var item in fileMatches) + { + expectedFiles.ShouldContain(item); + } + } + finally + { + if (Directory.Exists(symlinkPath)) + { + Directory.Delete(symlinkPath); + } + } + } #endif [Theory] diff --git a/src/Framework/Utilities/FileMatcher.cs b/src/Framework/Utilities/FileMatcher.cs index b6411ec342e..4b0e624316a 100644 --- a/src/Framework/Utilities/FileMatcher.cs +++ b/src/Framework/Utilities/FileMatcher.cs @@ -838,7 +838,7 @@ private void GetFilesRecursive( try { FileSystemInfo linkTarget = Directory.ResolveLinkTarget(recursionState.BaseDirectory, returnFinalTarget: true); - if (linkTarget is not null && recursionState.BaseDirectory.Contains(linkTarget.FullName)) + if (linkTarget is not null && IsSubdirectoryOf(recursionState.BaseDirectory, linkTarget.FullName)) { return; } From c354e2975e8da10a0793695655b02d2bd103f4da Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 29 May 2026 09:36:46 +0000 Subject: [PATCH 2/2] Use FileUtilities.PathComparison for symlink cycle subdirectory guard Co-authored-by: GangWang01 <2950449+GangWang01@users.noreply.github.com> --- src/Framework/Utilities/FileMatcher.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Framework/Utilities/FileMatcher.cs b/src/Framework/Utilities/FileMatcher.cs index 4b0e624316a..20a8a0374dc 100644 --- a/src/Framework/Utilities/FileMatcher.cs +++ b/src/Framework/Utilities/FileMatcher.cs @@ -838,7 +838,7 @@ private void GetFilesRecursive( try { FileSystemInfo linkTarget = Directory.ResolveLinkTarget(recursionState.BaseDirectory, returnFinalTarget: true); - if (linkTarget is not null && IsSubdirectoryOf(recursionState.BaseDirectory, linkTarget.FullName)) + if (linkTarget is not null && IsSubdirectoryOf(recursionState.BaseDirectory, linkTarget.FullName, FileUtilities.PathComparison)) { return; } @@ -2647,7 +2647,7 @@ private bool InnerExceptionsAreAllIoRelated(AggregateException ex) return ex.Flatten().InnerExceptions.All(ExceptionHandling.IsIoRelatedException); } - private static bool IsSubdirectoryOf(string possibleChild, string possibleParent) + private static bool IsSubdirectoryOf(string possibleChild, string possibleParent, StringComparison pathComparison = StringComparison.OrdinalIgnoreCase) { if (possibleParent == string.Empty) { @@ -2655,7 +2655,7 @@ private static bool IsSubdirectoryOf(string possibleChild, string possibleParent return true; } - bool prefixMatch = possibleChild.StartsWith(possibleParent, StringComparison.OrdinalIgnoreCase); + bool prefixMatch = possibleChild.StartsWith(possibleParent, pathComparison); if (!prefixMatch) { return false;