mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-11 02:20:48 -07:00
@@ -19,6 +19,7 @@ import {
|
||||
deduplicateAbsolutePaths,
|
||||
toAbsolutePath,
|
||||
toPathKey,
|
||||
isTrustedSystemPath,
|
||||
} from './paths.js';
|
||||
|
||||
vi.mock('node:fs', async (importOriginal) => {
|
||||
@@ -797,4 +798,61 @@ describe('normalizePath', () => {
|
||||
expect(toPathKey('/Tmp/Foo')).toBe(path.normalize('/Tmp/Foo'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('isTrustedSystemPath', () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
it('should reject paths in the current working directory', () => {
|
||||
const cwd = process.cwd();
|
||||
expect(isTrustedSystemPath(path.join(cwd, 'bin/rg'))).toBe(false);
|
||||
expect(isTrustedSystemPath(cwd)).toBe(false);
|
||||
});
|
||||
|
||||
it('should allow trusted paths on Windows', () => {
|
||||
mockPlatform('win32');
|
||||
vi.stubEnv('SystemRoot', 'C:\\Windows');
|
||||
vi.stubEnv('ProgramFiles', 'C:\\Program Files');
|
||||
vi.stubEnv('ProgramFiles(x86)', 'C:\\Program Files (x86)');
|
||||
|
||||
expect(isTrustedSystemPath('C:\\Windows\\System32\\rg.exe')).toBe(true);
|
||||
expect(isTrustedSystemPath('C:\\Program Files\\ripgrep\\rg.exe')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
isTrustedSystemPath('C:\\Program Files (x86)\\ripgrep\\rg.exe'),
|
||||
).toBe(true);
|
||||
|
||||
// Case insensitive
|
||||
expect(isTrustedSystemPath('c:\\windows\\system32\\rg.exe')).toBe(true);
|
||||
|
||||
// Untrusted paths
|
||||
expect(isTrustedSystemPath('D:\\Downloads\\rg.exe')).toBe(false);
|
||||
expect(isTrustedSystemPath('C:\\Users\\User\\rg.exe')).toBe(false);
|
||||
});
|
||||
|
||||
it('should allow trusted paths on macOS and Linux', () => {
|
||||
mockPlatform('darwin');
|
||||
|
||||
expect(isTrustedSystemPath('/usr/bin/rg')).toBe(true);
|
||||
expect(isTrustedSystemPath('/bin/rg')).toBe(true);
|
||||
expect(isTrustedSystemPath('/usr/local/bin/rg')).toBe(true);
|
||||
expect(isTrustedSystemPath('/opt/homebrew/bin/rg')).toBe(true);
|
||||
expect(
|
||||
isTrustedSystemPath('/opt/homebrew/Cellar/ripgrep/13.0.0/bin/rg'),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isTrustedSystemPath('/usr/local/Cellar/ripgrep/13.0.0/bin/rg'),
|
||||
).toBe(true);
|
||||
expect(isTrustedSystemPath('/usr/sbin/rg')).toBe(true);
|
||||
expect(isTrustedSystemPath('/sbin/rg')).toBe(true);
|
||||
|
||||
// Untrusted paths
|
||||
expect(isTrustedSystemPath('/home/user/bin/rg')).toBe(false);
|
||||
expect(isTrustedSystemPath('/tmp/rg')).toBe(false);
|
||||
expect(isTrustedSystemPath('/Library/rg')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -512,3 +512,47 @@ export function toPathKey(p: string): string {
|
||||
const isCaseInsensitive = platform === 'win32' || platform === 'darwin';
|
||||
return isCaseInsensitive ? norm.toLowerCase() : norm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if a path is a trusted system directory.
|
||||
*/
|
||||
export function isTrustedSystemPath(filePath: string): boolean {
|
||||
const normPath = normalizePath(filePath);
|
||||
|
||||
// 1. Explicitly reject paths in current working directory to prevent RCE
|
||||
// Exclude root directories to avoid inadvertently rejecting all system paths.
|
||||
const normCwd = normalizePath(process.cwd());
|
||||
const isRoot = normCwd === '/' || /^[a-zA-Z]:[\\/]?$/.test(normCwd);
|
||||
if (!isRoot && isSubpath(normCwd, normPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Allow standard system directories
|
||||
const platform = process.platform;
|
||||
if (platform === 'win32') {
|
||||
const trustedPrefixes = [
|
||||
process.env['SystemRoot'] || 'C:\\Windows',
|
||||
process.env['ProgramFiles'] || 'C:\\Program Files',
|
||||
process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)',
|
||||
].map((p) => normalizePath(p));
|
||||
|
||||
return trustedPrefixes.some(
|
||||
(prefix) => normPath === prefix || normPath.startsWith(prefix + '/'),
|
||||
);
|
||||
} else {
|
||||
const trustedPrefixes = [
|
||||
'/usr/bin',
|
||||
'/bin',
|
||||
'/usr/local/bin',
|
||||
'/opt/homebrew/bin',
|
||||
'/opt/homebrew/Cellar',
|
||||
'/usr/local/Cellar',
|
||||
'/usr/sbin',
|
||||
'/sbin',
|
||||
].map((p) => normalizePath(p));
|
||||
|
||||
return trustedPrefixes.some(
|
||||
(prefix) => normPath === prefix || normPath.startsWith(prefix + '/'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user