|
6 | 6 |
|
7 | 7 | import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; |
8 | 8 | import * as fs from 'node:fs/promises'; |
| 9 | +import * as fsSync from 'node:fs'; |
| 10 | +import * as os from 'node:os'; |
9 | 11 | import * as path from 'node:path'; |
10 | 12 | import { marked } from 'marked'; |
11 | 13 | import { processImports, validateImportPath } from './memoryImportProcessor.js'; |
@@ -867,5 +869,46 @@ describe('memoryImportProcessor', () => { |
867 | 869 | ); |
868 | 870 | expect(validateImportPath(dotPath, basePath, [allowedPath])).toBe(true); |
869 | 871 | }); |
| 872 | + |
| 873 | + it('should reject paths that escape allowed directories via symbolic links', () => { |
| 874 | + const tmpDir = fsSync.realpathSync(os.tmpdir()); |
| 875 | + const testRoot = fsSync.mkdtempSync(path.join(tmpDir, 'gemini-test-')); |
| 876 | + const allowedDir = path.join(testRoot, 'allowed'); |
| 877 | + const outsideDir = path.join(testRoot, 'outside'); |
| 878 | + const symlinkDir = path.join(allowedDir, 'sym_outside'); |
| 879 | + |
| 880 | + try { |
| 881 | + // Create real directories and files on disk |
| 882 | + fsSync.mkdirSync(allowedDir, { recursive: true }); |
| 883 | + fsSync.mkdirSync(outsideDir, { recursive: true }); |
| 884 | + fsSync.writeFileSync(path.join(outsideDir, 'sensitive.md'), 'secret'); |
| 885 | + |
| 886 | + // Create a symbolic link pointing outside the allowed directory |
| 887 | + try { |
| 888 | + fsSync.symlinkSync(outsideDir, symlinkDir, 'dir'); |
| 889 | + } catch (err: unknown) { |
| 890 | + if ( |
| 891 | + process.platform === 'win32' && |
| 892 | + err && |
| 893 | + typeof err === 'object' && |
| 894 | + 'code' in err && |
| 895 | + err.code === 'EPERM' |
| 896 | + ) { |
| 897 | + // Skip the test if the user lacks symlink creation privileges on Windows |
| 898 | + return; |
| 899 | + } |
| 900 | + throw err; |
| 901 | + } |
| 902 | + |
| 903 | + const importPath = 'sym_outside/sensitive.md'; |
| 904 | + |
| 905 | + expect(validateImportPath(importPath, allowedDir, [allowedDir])).toBe( |
| 906 | + false, |
| 907 | + ); |
| 908 | + } finally { |
| 909 | + // Cleanup |
| 910 | + fsSync.rmSync(testRoot, { recursive: true, force: true }); |
| 911 | + } |
| 912 | + }); |
870 | 913 | }); |
871 | 914 | }); |
0 commit comments