mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-17 16:51:15 -07:00
Add Folder Trust Support To Hooks (#15325)
This commit is contained in:
@@ -11,6 +11,8 @@ import { HookEventName, HookType } from './types.js';
|
||||
import type { HookConfig } from './types.js';
|
||||
import type { HookInput } from './types.js';
|
||||
import type { Readable, Writable } from 'node:stream';
|
||||
import type { Config } from '../config/config.js';
|
||||
import { ConfigSource } from './types.js';
|
||||
|
||||
// Mock type for the child_process spawn
|
||||
type MockChildProcessWithoutNullStreams = ChildProcessWithoutNullStreams & {
|
||||
@@ -53,6 +55,7 @@ vi.stubGlobal('console', mockConsole);
|
||||
describe('HookRunner', () => {
|
||||
let hookRunner: HookRunner;
|
||||
let mockSpawn: MockChildProcessWithoutNullStreams;
|
||||
let mockConfig: Config;
|
||||
|
||||
const mockInput: HookInput = {
|
||||
session_id: 'test-session',
|
||||
@@ -65,7 +68,11 @@ describe('HookRunner', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
|
||||
hookRunner = new HookRunner();
|
||||
mockConfig = {
|
||||
isTrustedFolder: vi.fn().mockReturnValue(true),
|
||||
} as unknown as Config;
|
||||
|
||||
hookRunner = new HookRunner(mockConfig);
|
||||
|
||||
// Mock spawn with accessible mock functions
|
||||
const mockStdoutOn = vi.fn();
|
||||
@@ -100,6 +107,89 @@ describe('HookRunner', () => {
|
||||
});
|
||||
|
||||
describe('executeHook', () => {
|
||||
describe('security checks', () => {
|
||||
it('should block project hooks in untrusted folders', async () => {
|
||||
vi.mocked(mockConfig.isTrustedFolder).mockReturnValue(false);
|
||||
|
||||
const projectHookConfig: HookConfig = {
|
||||
type: HookType.Command,
|
||||
command: './hooks/test.sh',
|
||||
source: ConfigSource.Project,
|
||||
};
|
||||
|
||||
const result = await hookRunner.executeHook(
|
||||
projectHookConfig,
|
||||
HookEventName.BeforeTool,
|
||||
mockInput,
|
||||
);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error?.message).toContain(
|
||||
'Security: Blocked execution of project hook in untrusted folder',
|
||||
);
|
||||
expect(mockDebugLogger.warn).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Security: Blocked execution'),
|
||||
);
|
||||
expect(spawn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should allow project hooks in trusted folders', async () => {
|
||||
vi.mocked(mockConfig.isTrustedFolder).mockReturnValue(true);
|
||||
|
||||
const projectHookConfig: HookConfig = {
|
||||
type: HookType.Command,
|
||||
command: './hooks/test.sh',
|
||||
source: ConfigSource.Project,
|
||||
};
|
||||
|
||||
// Mock successful execution
|
||||
mockSpawn.mockProcessOn.mockImplementation(
|
||||
(event: string, callback: (code: number) => void) => {
|
||||
if (event === 'close') {
|
||||
setTimeout(() => callback(0), 10);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const result = await hookRunner.executeHook(
|
||||
projectHookConfig,
|
||||
HookEventName.BeforeTool,
|
||||
mockInput,
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(spawn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should allow non-project hooks even in untrusted folders', async () => {
|
||||
vi.mocked(mockConfig.isTrustedFolder).mockReturnValue(false);
|
||||
|
||||
const systemHookConfig: HookConfig = {
|
||||
type: HookType.Command,
|
||||
command: './hooks/test.sh',
|
||||
source: ConfigSource.System,
|
||||
};
|
||||
|
||||
// Mock successful execution
|
||||
mockSpawn.mockProcessOn.mockImplementation(
|
||||
(event: string, callback: (code: number) => void) => {
|
||||
if (event === 'close') {
|
||||
setTimeout(() => callback(0), 10);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const result = await hookRunner.executeHook(
|
||||
systemHookConfig,
|
||||
HookEventName.BeforeTool,
|
||||
mockInput,
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(spawn).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('command hooks', () => {
|
||||
const commandConfig: HookConfig = {
|
||||
type: HookType.Command,
|
||||
|
||||
Reference in New Issue
Block a user