diff options
Diffstat (limited to 'packages/core/src/utils')
| -rw-r--r-- | packages/core/src/utils/memoryImportProcessor.ts | 10 | ||||
| -rw-r--r-- | packages/core/src/utils/paths.test.ts | 106 | ||||
| -rw-r--r-- | packages/core/src/utils/paths.ts | 20 |
3 files changed, 128 insertions, 8 deletions
diff --git a/packages/core/src/utils/memoryImportProcessor.ts b/packages/core/src/utils/memoryImportProcessor.ts index 751e0ace..c89b983b 100644 --- a/packages/core/src/utils/memoryImportProcessor.ts +++ b/packages/core/src/utils/memoryImportProcessor.ts @@ -6,6 +6,7 @@ import * as fs from 'fs/promises'; import * as path from 'path'; +import { isSubpath } from './paths.js'; import { marked } from 'marked'; // Simple console logger for import processing @@ -411,10 +412,7 @@ export function validateImportPath( const resolvedPath = path.resolve(basePath, importPath); - return allowedDirectories.some((allowedDir) => { - const normalizedAllowedDir = path.resolve(allowedDir); - const isSamePath = resolvedPath === normalizedAllowedDir; - const isSubPath = resolvedPath.startsWith(normalizedAllowedDir + path.sep); - return isSamePath || isSubPath; - }); + return allowedDirectories.some((allowedDir) => + isSubpath(allowedDir, resolvedPath), + ); } diff --git a/packages/core/src/utils/paths.test.ts b/packages/core/src/utils/paths.test.ts index d688c072..0e964672 100644 --- a/packages/core/src/utils/paths.test.ts +++ b/packages/core/src/utils/paths.test.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { describe, it, expect } from 'vitest'; -import { escapePath, unescapePath } from './paths.js'; +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { escapePath, unescapePath, isSubpath } from './paths.js'; describe('escapePath', () => { it('should escape spaces', () => { @@ -212,3 +212,105 @@ describe('unescapePath', () => { expect(unescapePath('file\\\\\\(test\\).txt')).toBe('file\\\\(test).txt'); }); }); + +describe('isSubpath', () => { + it('should return true for a direct subpath', () => { + expect(isSubpath('/a/b', '/a/b/c')).toBe(true); + }); + + it('should return true for the same path', () => { + expect(isSubpath('/a/b', '/a/b')).toBe(true); + }); + + it('should return false for a parent path', () => { + expect(isSubpath('/a/b/c', '/a/b')).toBe(false); + }); + + it('should return false for a completely different path', () => { + expect(isSubpath('/a/b', '/x/y')).toBe(false); + }); + + it('should handle relative paths', () => { + expect(isSubpath('a/b', 'a/b/c')).toBe(true); + expect(isSubpath('a/b', 'a/c')).toBe(false); + }); + + it('should handle paths with ..', () => { + expect(isSubpath('/a/b', '/a/b/../b/c')).toBe(true); + expect(isSubpath('/a/b', '/a/c/../b')).toBe(true); + }); + + it('should handle root paths', () => { + expect(isSubpath('/', '/a')).toBe(true); + expect(isSubpath('/a', '/')).toBe(false); + }); + + it('should handle trailing slashes', () => { + expect(isSubpath('/a/b/', '/a/b/c')).toBe(true); + expect(isSubpath('/a/b', '/a/b/c/')).toBe(true); + expect(isSubpath('/a/b/', '/a/b/c/')).toBe(true); + }); +}); + +describe('isSubpath on Windows', () => { + const originalPlatform = process.platform; + + beforeAll(() => { + Object.defineProperty(process, 'platform', { + value: 'win32', + }); + }); + + afterAll(() => { + Object.defineProperty(process, 'platform', { + value: originalPlatform, + }); + }); + + it('should return true for a direct subpath on Windows', () => { + expect(isSubpath('C:\\Users\\Test', 'C:\\Users\\Test\\file.txt')).toBe( + true, + ); + }); + + it('should return true for the same path on Windows', () => { + expect(isSubpath('C:\\Users\\Test', 'C:\\Users\\Test')).toBe(true); + }); + + it('should return false for a parent path on Windows', () => { + expect(isSubpath('C:\\Users\\Test\\file.txt', 'C:\\Users\\Test')).toBe( + false, + ); + }); + + it('should return false for a different drive on Windows', () => { + expect(isSubpath('C:\\Users\\Test', 'D:\\Users\\Test')).toBe(false); + }); + + it('should be case-insensitive for drive letters on Windows', () => { + expect(isSubpath('c:\\Users\\Test', 'C:\\Users\\Test\\file.txt')).toBe( + true, + ); + }); + + it('should be case-insensitive for path components on Windows', () => { + expect(isSubpath('C:\\Users\\Test', 'c:\\users\\test\\file.txt')).toBe( + true, + ); + }); + + it('should handle mixed slashes on Windows', () => { + expect(isSubpath('C:/Users/Test', 'C:\\Users\\Test\\file.txt')).toBe(true); + }); + + it('should handle trailing slashes on Windows', () => { + expect(isSubpath('C:\\Users\\Test\\', 'C:\\Users\\Test\\file.txt')).toBe( + true, + ); + }); + + it('should handle relative paths correctly on Windows', () => { + expect(isSubpath('Users\\Test', 'Users\\Test\\file.txt')).toBe(true); + expect(isSubpath('Users\\Test\\file.txt', 'Users\\Test')).toBe(false); + }); +}); diff --git a/packages/core/src/utils/paths.ts b/packages/core/src/utils/paths.ts index 7bab888e..e7cf54cc 100644 --- a/packages/core/src/utils/paths.ts +++ b/packages/core/src/utils/paths.ts @@ -200,3 +200,23 @@ export function getUserCommandsDir(): string { export function getProjectCommandsDir(projectRoot: string): string { return path.join(projectRoot, GEMINI_DIR, COMMANDS_DIR_NAME); } + +/** + * Checks if a path is a subpath of another path. + * @param parentPath The parent path. + * @param childPath The child path. + * @returns True if childPath is a subpath of parentPath, false otherwise. + */ +export function isSubpath(parentPath: string, childPath: string): boolean { + const isWindows = os.platform() === 'win32'; + const pathModule = isWindows ? path.win32 : path; + + // On Windows, path.relative is case-insensitive. On POSIX, it's case-sensitive. + const relative = pathModule.relative(parentPath, childPath); + + return ( + !relative.startsWith(`..${pathModule.sep}`) && + relative !== '..' && + !pathModule.isAbsolute(relative) + ); +} |
