Ignore workspace settings for untrusted folders (#6606)

This commit is contained in:
shrutip90
2025-08-19 21:20:41 -07:00
committed by GitHub
parent 179f1414da
commit d250293c2e
16 changed files with 386 additions and 122 deletions

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { useState, useCallback, useEffect } from 'react';
import { useState, useCallback, useEffect, useContext } from 'react';
import { LoadedSettings, SettingScope } from '../../config/settings.js';
import {
AuthType,
@@ -13,6 +13,7 @@ import {
getErrorMessage,
} from '@google/gemini-cli-core';
import { runExitCleanup } from '../../utils/cleanup.js';
import { SettingsContext } from '../contexts/SettingsContext.js';
export const useAuthCommand = (
settings: LoadedSettings,
@@ -22,6 +23,7 @@ export const useAuthCommand = (
const [isAuthDialogOpen, setIsAuthDialogOpen] = useState(
settings.merged.selectedAuthType === undefined,
);
const settingsContext = useContext(SettingsContext);
const openAuthDialog = useCallback(() => {
setIsAuthDialogOpen(true);
@@ -56,7 +58,7 @@ export const useAuthCommand = (
if (authType) {
await clearCachedCredentialFile();
settings.setValue(scope, 'selectedAuthType', authType);
settingsContext?.settings.setValue(scope, 'selectedAuthType', authType);
if (
authType === AuthType.LOGIN_WITH_GOOGLE &&
config.isBrowserLaunchSuppressed()
@@ -75,7 +77,7 @@ Logging in with Google... Please restart Gemini CLI to continue.
setIsAuthDialogOpen(false);
setAuthError(null);
},
[settings, setAuthError, config],
[settingsContext, setAuthError, config],
);
const cancelAuthentication = useCallback(() => {

View File

@@ -23,6 +23,8 @@ import {
checkHasEditorType,
allowEditorTypeInSandbox,
} from '@google/gemini-cli-core';
import { SettingsContext } from '../contexts/SettingsContext.js';
import { type ReactNode } from 'react';
vi.mock('@google/gemini-cli-core', async () => {
const actual = await vi.importActual('@google/gemini-cli-core');
@@ -43,13 +45,23 @@ describe('useEditorSettings', () => {
(item: Omit<HistoryItem, 'id'>, timestamp: number) => void
>;
const wrapper = ({ children }: { children: ReactNode }) => (
<SettingsContext.Provider
value={{ settings: mockLoadedSettings, recomputeSettings: () => {} }}
>
{children}
</SettingsContext.Provider>
);
beforeEach(() => {
vi.resetAllMocks();
mockLoadedSettings = {
setValue: vi.fn(),
} as unknown as LoadedSettings;
mockLoadedSettings = new LoadedSettings(
{ path: '', settings: {} },
{ path: '', settings: {} },
{ path: '', settings: {} },
[],
);
mockLoadedSettings.setValue = vi.fn();
mockSetEditorError = vi.fn();
mockAddItem = vi.fn();
@@ -63,16 +75,18 @@ describe('useEditorSettings', () => {
});
it('should initialize with dialog closed', () => {
const { result } = renderHook(() =>
useEditorSettings(mockLoadedSettings, mockSetEditorError, mockAddItem),
const { result } = renderHook(
() => useEditorSettings(mockSetEditorError, mockAddItem),
{ wrapper },
);
expect(result.current.isEditorDialogOpen).toBe(false);
});
it('should open editor dialog when openEditorDialog is called', () => {
const { result } = renderHook(() =>
useEditorSettings(mockLoadedSettings, mockSetEditorError, mockAddItem),
const { result } = renderHook(
() => useEditorSettings(mockSetEditorError, mockAddItem),
{ wrapper },
);
act(() => {
@@ -83,8 +97,9 @@ describe('useEditorSettings', () => {
});
it('should close editor dialog when exitEditorDialog is called', () => {
const { result } = renderHook(() =>
useEditorSettings(mockLoadedSettings, mockSetEditorError, mockAddItem),
const { result } = renderHook(
() => useEditorSettings(mockSetEditorError, mockAddItem),
{ wrapper },
);
act(() => {
result.current.openEditorDialog();
@@ -94,8 +109,9 @@ describe('useEditorSettings', () => {
});
it('should handle editor selection successfully', () => {
const { result } = renderHook(() =>
useEditorSettings(mockLoadedSettings, mockSetEditorError, mockAddItem),
const { result } = renderHook(
() => useEditorSettings(mockSetEditorError, mockAddItem),
{ wrapper },
);
const editorType: EditorType = 'vscode';
@@ -125,8 +141,9 @@ describe('useEditorSettings', () => {
});
it('should handle clearing editor preference (undefined editor)', () => {
const { result } = renderHook(() =>
useEditorSettings(mockLoadedSettings, mockSetEditorError, mockAddItem),
const { result } = renderHook(
() => useEditorSettings(mockSetEditorError, mockAddItem),
{ wrapper },
);
const scope = SettingScope.Workspace;
@@ -155,8 +172,9 @@ describe('useEditorSettings', () => {
});
it('should handle different editor types', () => {
const { result } = renderHook(() =>
useEditorSettings(mockLoadedSettings, mockSetEditorError, mockAddItem),
const { result } = renderHook(
() => useEditorSettings(mockSetEditorError, mockAddItem),
{ wrapper },
);
const editorTypes: EditorType[] = ['cursor', 'windsurf', 'vim'];
@@ -184,8 +202,9 @@ describe('useEditorSettings', () => {
});
it('should handle different setting scopes', () => {
const { result } = renderHook(() =>
useEditorSettings(mockLoadedSettings, mockSetEditorError, mockAddItem),
const { result } = renderHook(
() => useEditorSettings(mockSetEditorError, mockAddItem),
{ wrapper },
);
const editorType: EditorType = 'vscode';
@@ -213,8 +232,9 @@ describe('useEditorSettings', () => {
});
it('should not set preference for unavailable editors', () => {
const { result } = renderHook(() =>
useEditorSettings(mockLoadedSettings, mockSetEditorError, mockAddItem),
const { result } = renderHook(
() => useEditorSettings(mockSetEditorError, mockAddItem),
{ wrapper },
);
mockCheckHasEditorType.mockReturnValue(false);
@@ -233,8 +253,9 @@ describe('useEditorSettings', () => {
});
it('should not set preference for editors not allowed in sandbox', () => {
const { result } = renderHook(() =>
useEditorSettings(mockLoadedSettings, mockSetEditorError, mockAddItem),
const { result } = renderHook(
() => useEditorSettings(mockSetEditorError, mockAddItem),
{ wrapper },
);
mockAllowEditorTypeInSandbox.mockReturnValue(false);
@@ -253,8 +274,9 @@ describe('useEditorSettings', () => {
});
it('should handle errors during editor selection', () => {
const { result } = renderHook(() =>
useEditorSettings(mockLoadedSettings, mockSetEditorError, mockAddItem),
const { result } = renderHook(
() => useEditorSettings(mockSetEditorError, mockAddItem),
{ wrapper },
);
const errorMessage = 'Failed to save settings';

View File

@@ -4,14 +4,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { useState, useCallback } from 'react';
import { LoadedSettings, SettingScope } from '../../config/settings.js';
import { useState, useCallback, useContext } from 'react';
import { SettingScope } from '../../config/settings.js';
import { type HistoryItem, MessageType } from '../types.js';
import {
allowEditorTypeInSandbox,
checkHasEditorType,
EditorType,
} from '@google/gemini-cli-core';
import { SettingsContext } from '../contexts/SettingsContext.js';
interface UseEditorSettingsReturn {
isEditorDialogOpen: boolean;
@@ -24,11 +25,11 @@ interface UseEditorSettingsReturn {
}
export const useEditorSettings = (
loadedSettings: LoadedSettings,
setEditorError: (error: string | null) => void,
addItem: (item: Omit<HistoryItem, 'id'>, timestamp: number) => void,
): UseEditorSettingsReturn => {
const [isEditorDialogOpen, setIsEditorDialogOpen] = useState(false);
const settingsContext = useContext(SettingsContext);
const openEditorDialog = useCallback(() => {
setIsEditorDialogOpen(true);
@@ -45,7 +46,11 @@ export const useEditorSettings = (
}
try {
loadedSettings.setValue(scope, 'preferredEditor', editorType);
settingsContext?.settings.setValue(
scope,
'preferredEditor',
editorType,
);
addItem(
{
type: MessageType.INFO,
@@ -59,7 +64,7 @@ export const useEditorSettings = (
setEditorError(`Failed to set editor preference: ${error}`);
}
},
[loadedSettings, setEditorError, addItem],
[settingsContext, setEditorError, addItem],
);
const exitEditorDialog = useCallback(() => {

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { useState, useCallback, useEffect } from 'react';
import { useState, useCallback, useEffect, useContext } from 'react';
import { Settings, LoadedSettings } from '../../config/settings.js';
import { FolderTrustChoice } from '../components/FolderTrustDialog.js';
import {
@@ -13,6 +13,7 @@ import {
isWorkspaceTrusted,
} from '../../config/trustedFolders.js';
import * as process from 'process';
import { SettingsContext } from '../contexts/SettingsContext.js';
export const useFolderTrust = (
settings: LoadedSettings,
@@ -20,6 +21,7 @@ export const useFolderTrust = (
) => {
const [isTrusted, setIsTrusted] = useState<boolean | undefined>(undefined);
const [isFolderTrustDialogOpen, setIsFolderTrustDialogOpen] = useState(false);
const settingsContext = useContext(SettingsContext);
const { folderTrust, folderTrustFeature } = settings.merged;
useEffect(() => {
@@ -60,8 +62,9 @@ export const useFolderTrust = (
setIsTrusted(trusted);
setIsFolderTrustDialogOpen(false);
onTrustChange(trusted);
settingsContext?.recomputeSettings();
},
[onTrustChange, folderTrust, folderTrustFeature],
[onTrustChange, folderTrust, folderTrustFeature, settingsContext],
);
return {

View File

@@ -4,10 +4,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { useState, useCallback, useEffect } from 'react';
import { useState, useCallback, useEffect, useContext } from 'react';
import { themeManager } from '../themes/theme-manager.js';
import { LoadedSettings, SettingScope } from '../../config/settings.js'; // Import LoadedSettings, AppSettings, MergedSetting
import { type HistoryItem, MessageType } from '../types.js';
import { HistoryItem, MessageType } from '../types.js';
import { SettingScope } from '../../config/settings.js';
import { SettingsContext } from '../contexts/SettingsContext.js';
import process from 'node:process';
interface UseThemeCommandReturn {
@@ -21,11 +22,12 @@ interface UseThemeCommandReturn {
}
export const useThemeCommand = (
loadedSettings: LoadedSettings,
setThemeError: (error: string | null) => void,
addItem: (item: Omit<HistoryItem, 'id'>, timestamp: number) => void,
): UseThemeCommandReturn => {
const [isThemeDialogOpen, setIsThemeDialogOpen] = useState(false);
const settingsContext = useContext(SettingsContext);
const loadedSettings = settingsContext!.settings;
// Check for invalid theme configuration on startup
useEffect(() => {