fix: default folder trust to untrusted for enhanced security (#15943)

This commit is contained in:
Gal Zahavi
2026-01-06 10:09:09 -08:00
committed by GitHub
parent 9a3ff6510f
commit 6f4b2ad0b9
6 changed files with 103 additions and 78 deletions

View File

@@ -7,7 +7,7 @@
import { renderWithProviders } from '../../test-utils/render.js';
import { waitFor } from '../../test-utils/async.js';
import { act } from 'react';
import { vi } from 'vitest';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { FolderTrustDialog } from './FolderTrustDialog.js';
import { ExitCodes } from '@google/gemini-cli-core';
import * as processUtils from '../../utils/processUtils.js';
@@ -74,7 +74,7 @@ describe('FolderTrustDialog', () => {
<FolderTrustDialog onSelect={vi.fn()} isRestarting={true} />,
);
expect(lastFrame()).toContain(' Gemini CLI is restarting');
expect(lastFrame()).toContain('Gemini CLI is restarting');
});
it('should call relaunchApp when isRestarting is true', async () => {
@@ -88,6 +88,21 @@ describe('FolderTrustDialog', () => {
vi.useRealTimers();
});
it('should not call relaunchApp if unmounted before timeout', async () => {
vi.useFakeTimers();
const relaunchApp = vi.spyOn(processUtils, 'relaunchApp');
const { unmount } = renderWithProviders(
<FolderTrustDialog onSelect={vi.fn()} isRestarting={true} />,
);
// Unmount immediately (before 250ms)
unmount();
await vi.advanceTimersByTimeAsync(250);
expect(relaunchApp).not.toHaveBeenCalled();
vi.useRealTimers();
});
it('should not call process.exit when "r" is pressed and isRestarting is false', async () => {
const { stdin } = renderWithProviders(
<FolderTrustDialog onSelect={vi.fn()} isRestarting={false} />,

View File

@@ -6,7 +6,7 @@
import { Box, Text } from 'ink';
import type React from 'react';
import { useEffect, useState } from 'react';
import { useEffect, useState, useCallback } from 'react';
import { theme } from '../semantic-colors.js';
import type { RadioSelectItem } from './shared/RadioButtonSelect.js';
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
@@ -33,26 +33,32 @@ export const FolderTrustDialog: React.FC<FolderTrustDialogProps> = ({
isRestarting,
}) => {
const [exiting, setExiting] = useState(false);
useEffect(() => {
const doRelaunch = async () => {
if (isRestarting) {
setTimeout(async () => {
await relaunchApp();
}, 250);
}
let timer: ReturnType<typeof setTimeout>;
if (isRestarting) {
timer = setTimeout(async () => {
await relaunchApp();
}, 250);
}
return () => {
if (timer) clearTimeout(timer);
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
doRelaunch();
}, [isRestarting]);
const handleExit = useCallback(() => {
setExiting(true);
// Give time for the UI to render the exiting message
setTimeout(async () => {
await runExitCleanup();
process.exit(ExitCodes.FATAL_CANCELLATION_ERROR);
}, 100);
}, []);
useKeypress(
(key) => {
if (key.name === 'escape') {
setExiting(true);
setTimeout(async () => {
await runExitCleanup();
process.exit(ExitCodes.FATAL_CANCELLATION_ERROR);
}, 100);
handleExit();
}
},
{ isActive: !isRestarting },