From a044c25981d7ae74fa1cd42cb002ed721b65c7a0 Mon Sep 17 00:00:00 2001 From: shrutip90 Date: Wed, 8 Oct 2025 22:17:58 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20Add=20a=20message=20about=20permissions?= =?UTF-8?q?=20command=20on=20startup=20in=20untrusted=20=E2=80=A6=20(#1075?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cli/src/ui/AppContainer.tsx | 2 +- .../cli/src/ui/hooks/useFolderTrust.test.ts | 38 ++++++++++++++----- packages/cli/src/ui/hooks/useFolderTrust.ts | 18 ++++++++- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index 3d35b1738d..b11ef38863 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -796,7 +796,7 @@ Logging in with Google... Please restart Gemini CLI to continue. const [showIdeRestartPrompt, setShowIdeRestartPrompt] = useState(false); const { isFolderTrustDialogOpen, handleFolderTrustSelect, isRestarting } = - useFolderTrust(settings, setIsTrustedFolder); + useFolderTrust(settings, setIsTrustedFolder, historyManager.addItem); const { needsRestart: ideNeedsRestart, restartReason: ideTrustRestartReason, diff --git a/packages/cli/src/ui/hooks/useFolderTrust.test.ts b/packages/cli/src/ui/hooks/useFolderTrust.test.ts index 211b2d524c..4812efc38b 100644 --- a/packages/cli/src/ui/hooks/useFolderTrust.test.ts +++ b/packages/cli/src/ui/hooks/useFolderTrust.test.ts @@ -31,6 +31,7 @@ describe('useFolderTrust', () => { let loadTrustedFoldersSpy: vi.SpyInstance; let isWorkspaceTrustedSpy: vi.SpyInstance; let onTrustChange: (isTrusted: boolean | undefined) => void; + let addItem: vi.Mock; beforeEach(() => { mockSettings = { @@ -54,6 +55,7 @@ describe('useFolderTrust', () => { isWorkspaceTrustedSpy = vi.spyOn(trustedFolders, 'isWorkspaceTrusted'); mockedCwd.mockReturnValue('/test/path'); onTrustChange = vi.fn(); + addItem = vi.fn(); }); afterEach(() => { @@ -63,7 +65,7 @@ describe('useFolderTrust', () => { it('should not open dialog when folder is already trusted', () => { isWorkspaceTrustedSpy.mockReturnValue({ isTrusted: true, source: 'file' }); const { result } = renderHook(() => - useFolderTrust(mockSettings, onTrustChange), + useFolderTrust(mockSettings, onTrustChange, addItem), ); expect(result.current.isFolderTrustDialogOpen).toBe(false); expect(onTrustChange).toHaveBeenCalledWith(true); @@ -72,7 +74,7 @@ describe('useFolderTrust', () => { it('should not open dialog when folder is already untrusted', () => { isWorkspaceTrustedSpy.mockReturnValue({ isTrusted: false, source: 'file' }); const { result } = renderHook(() => - useFolderTrust(mockSettings, onTrustChange), + useFolderTrust(mockSettings, onTrustChange, addItem), ); expect(result.current.isFolderTrustDialogOpen).toBe(false); expect(onTrustChange).toHaveBeenCalledWith(false); @@ -84,19 +86,37 @@ describe('useFolderTrust', () => { source: undefined, }); const { result } = renderHook(() => - useFolderTrust(mockSettings, onTrustChange), + useFolderTrust(mockSettings, onTrustChange, addItem), ); expect(result.current.isFolderTrustDialogOpen).toBe(true); expect(onTrustChange).toHaveBeenCalledWith(undefined); }); + it('should send a message if the folder is untrusted', () => { + isWorkspaceTrustedSpy.mockReturnValue({ isTrusted: false, source: 'file' }); + renderHook(() => useFolderTrust(mockSettings, onTrustChange, addItem)); + expect(addItem).toHaveBeenCalledWith( + { + text: 'This folder is not trusted. Some features may be disabled. Use the `/permissions` command to change the trust level.', + type: 'info', + }, + expect.any(Number), + ); + }); + + it('should not send a message if the folder is trusted', () => { + isWorkspaceTrustedSpy.mockReturnValue({ isTrusted: true, source: 'file' }); + renderHook(() => useFolderTrust(mockSettings, onTrustChange, addItem)); + expect(addItem).not.toHaveBeenCalled(); + }); + it('should handle TRUST_FOLDER choice', () => { isWorkspaceTrustedSpy.mockReturnValue({ isTrusted: undefined, source: undefined, }); const { result } = renderHook(() => - useFolderTrust(mockSettings, onTrustChange), + useFolderTrust(mockSettings, onTrustChange, addItem), ); act(() => { @@ -118,7 +138,7 @@ describe('useFolderTrust', () => { source: undefined, }); const { result } = renderHook(() => - useFolderTrust(mockSettings, onTrustChange), + useFolderTrust(mockSettings, onTrustChange, addItem), ); act(() => { @@ -139,7 +159,7 @@ describe('useFolderTrust', () => { source: undefined, }); const { result } = renderHook(() => - useFolderTrust(mockSettings, onTrustChange), + useFolderTrust(mockSettings, onTrustChange, addItem), ); act(() => { @@ -161,7 +181,7 @@ describe('useFolderTrust', () => { source: undefined, }); const { result } = renderHook(() => - useFolderTrust(mockSettings, onTrustChange), + useFolderTrust(mockSettings, onTrustChange, addItem), ); act(() => { @@ -179,7 +199,7 @@ describe('useFolderTrust', () => { it('should set isRestarting to true when trust status changes from false to true', () => { isWorkspaceTrustedSpy.mockReturnValue({ isTrusted: false, source: 'file' }); // Initially untrusted const { result } = renderHook(() => - useFolderTrust(mockSettings, onTrustChange), + useFolderTrust(mockSettings, onTrustChange, addItem), ); act(() => { @@ -196,7 +216,7 @@ describe('useFolderTrust', () => { source: undefined, }); const { result } = renderHook(() => - useFolderTrust(mockSettings, onTrustChange), + useFolderTrust(mockSettings, onTrustChange, addItem), ); act(() => { diff --git a/packages/cli/src/ui/hooks/useFolderTrust.ts b/packages/cli/src/ui/hooks/useFolderTrust.ts index 76d63150d7..55b57faed4 100644 --- a/packages/cli/src/ui/hooks/useFolderTrust.ts +++ b/packages/cli/src/ui/hooks/useFolderTrust.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { useState, useCallback, useEffect } from 'react'; +import { useState, useCallback, useEffect, useRef } from 'react'; import type { LoadedSettings } from '../../config/settings.js'; import { FolderTrustChoice } from '../components/FolderTrustDialog.js'; import { @@ -13,14 +13,17 @@ import { isWorkspaceTrusted, } from '../../config/trustedFolders.js'; import * as process from 'node:process'; +import { type HistoryItemWithoutId, MessageType } from '../types.js'; export const useFolderTrust = ( settings: LoadedSettings, onTrustChange: (isTrusted: boolean | undefined) => void, + addItem: (item: HistoryItemWithoutId, timestamp: number) => number, ) => { const [isTrusted, setIsTrusted] = useState(undefined); const [isFolderTrustDialogOpen, setIsFolderTrustDialogOpen] = useState(false); const [isRestarting, setIsRestarting] = useState(false); + const startupMessageSent = useRef(false); const folderTrust = settings.merged.security?.folderTrust?.enabled; @@ -29,7 +32,18 @@ export const useFolderTrust = ( setIsTrusted(trusted); setIsFolderTrustDialogOpen(trusted === undefined); onTrustChange(trusted); - }, [folderTrust, onTrustChange, settings.merged]); + + if (trusted === false && !startupMessageSent.current) { + addItem( + { + type: MessageType.INFO, + text: 'This folder is not trusted. Some features may be disabled. Use the `/permissions` command to change the trust level.', + }, + Date.now(), + ); + startupMessageSent.current = true; + } + }, [folderTrust, onTrustChange, settings.merged, addItem]); const handleFolderTrustSelect = useCallback( (choice: FolderTrustChoice) => {