From dfbc83cedfa2c637db0f93d98d7198faf7e59747 Mon Sep 17 00:00:00 2001 From: Sean Doherty Date: Sun, 17 May 2026 01:56:57 -0500 Subject: [PATCH] fix(filesystem): apply search excludes before realpath --- src/filesystem/__tests__/lib.test.ts | 34 ++++++++++++++++++++++++++++ src/filesystem/lib.ts | 13 +++++------ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/filesystem/__tests__/lib.test.ts b/src/filesystem/__tests__/lib.test.ts index bfe8987bfd..a272f00f8f 100644 --- a/src/filesystem/__tests__/lib.test.ts +++ b/src/filesystem/__tests__/lib.test.ts @@ -325,6 +325,40 @@ describe('Lib Functions', () => { expect(result).toEqual([expectedResult]); }); + it('skips excluded entries before validating their real paths', async () => { + const mockEntries = [ + { name: 'GoogleDrive-lazy', isDirectory: () => true }, + { name: 'match.txt', isDirectory: () => false } + ]; + + mockFs.readdir.mockResolvedValueOnce(mockEntries as any); + + const testDir = process.platform === 'win32' ? 'C:\\allowed\\dir' : '/allowed/dir'; + const allowedDirs = process.platform === 'win32' ? ['C:\\allowed'] : ['/allowed']; + + mockFs.realpath.mockImplementation(async (inputPath: any) => { + const pathStr = inputPath.toString(); + if (pathStr.includes('GoogleDrive-lazy')) { + throw new Error('excluded entries should not be realpathed'); + } + return pathStr; + }); + + const result = await searchFilesWithValidation( + testDir, + '*', + allowedDirs, + { excludePatterns: ['GoogleDrive-*'] } + ); + + const expectedResult = process.platform === 'win32' ? 'C:\\allowed\\dir\\match.txt' : '/allowed/dir/match.txt'; + expect(result).toEqual([expectedResult]); + const validatedExcludedEntry = mockFs.realpath.mock.calls.some(([inputPath]: any[]) => + inputPath.toString().includes('GoogleDrive-lazy') + ); + expect(validatedExcludedEntry).toBe(false); + }); + it('handles validation errors during search', async () => { const mockEntries = [ { name: 'test.txt', isDirectory: () => false }, diff --git a/src/filesystem/lib.ts b/src/filesystem/lib.ts index 240ca0d476..f1ae3b65f2 100644 --- a/src/filesystem/lib.ts +++ b/src/filesystem/lib.ts @@ -362,17 +362,16 @@ export async function searchFilesWithValidation( for (const entry of entries) { const fullPath = path.join(currentPath, entry.name); + const relativePath = path.relative(rootPath, fullPath); + const shouldExclude = excludePatterns.some(excludePattern => + minimatch(relativePath, excludePattern, { dot: true }) + ); + + if (shouldExclude) continue; try { await validatePath(fullPath); - const relativePath = path.relative(rootPath, fullPath); - const shouldExclude = excludePatterns.some(excludePattern => - minimatch(relativePath, excludePattern, { dot: true }) - ); - - if (shouldExclude) continue; - // Use glob matching for the search pattern if (minimatch(relativePath, pattern, { dot: true })) { results.push(fullPath);