diff options
| author | shrutip90 <[email protected]> | 2025-08-21 00:38:12 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-08-21 07:38:12 +0000 |
| commit | ba5309c4050efde8b0be0d9dd726e5c5f1a4c4c6 (patch) | |
| tree | d34cc814b2968bc8a09add14128544afe0f4f529 /packages/cli/src/config | |
| parent | 0242ecd83a5b0673a6ef97a406a743b286ef12e2 (diff) | |
Force restart on trust level change to reload settings (#6713)
Diffstat (limited to 'packages/cli/src/config')
| -rw-r--r-- | packages/cli/src/config/settings.test.ts | 64 | ||||
| -rw-r--r-- | packages/cli/src/config/settings.ts | 26 |
2 files changed, 84 insertions, 6 deletions
diff --git a/packages/cli/src/config/settings.test.ts b/packages/cli/src/config/settings.test.ts index 65a556be..83580542 100644 --- a/packages/cli/src/config/settings.test.ts +++ b/packages/cli/src/config/settings.test.ts @@ -27,6 +27,11 @@ vi.mock('./settings.js', async (importActual) => { }; }); +// Mock trustedFolders +vi.mock('./trustedFolders.js', () => ({ + isWorkspaceTrusted: vi.fn(), +})); + // NOW import everything else, including the (now effectively re-exported) settings.js import * as pathActual from 'path'; // Restored for MOCK_WORKSPACE_SETTINGS_PATH import { @@ -41,6 +46,7 @@ import { } from 'vitest'; import * as fs from 'fs'; // fs will be mocked separately import stripJsonComments from 'strip-json-comments'; // Will be mocked separately +import { isWorkspaceTrusted } from './trustedFolders.js'; // These imports will get the versions from the vi.mock('./settings.js', ...) factory. import { @@ -97,6 +103,7 @@ describe('Settings Loading and Merging', () => { (mockFsExistsSync as Mock).mockReturnValue(false); (fs.readFileSync as Mock).mockReturnValue('{}'); // Return valid empty JSON (mockFsMkdirSync as Mock).mockImplementation(() => undefined); + vi.mocked(isWorkspaceTrusted).mockReturnValue(true); }); afterEach(() => { @@ -1421,4 +1428,61 @@ describe('Settings Loading and Merging', () => { ]); }); }); + + describe('with workspace trust', () => { + it('should merge workspace settings when workspace is trusted', () => { + (mockFsExistsSync as Mock).mockReturnValue(true); + const userSettingsContent = { theme: 'dark', sandbox: false }; + const workspaceSettingsContent = { + sandbox: true, + contextFileName: 'WORKSPACE.md', + }; + + (fs.readFileSync as Mock).mockImplementation( + (p: fs.PathOrFileDescriptor) => { + if (p === USER_SETTINGS_PATH) + return JSON.stringify(userSettingsContent); + if (p === MOCK_WORKSPACE_SETTINGS_PATH) + return JSON.stringify(workspaceSettingsContent); + return '{}'; + }, + ); + + const settings = loadSettings(MOCK_WORKSPACE_DIR); + + expect(settings.merged.sandbox).toBe(true); + expect(settings.merged.contextFileName).toBe('WORKSPACE.md'); + expect(settings.merged.theme).toBe('dark'); + }); + + it('should NOT merge workspace settings when workspace is not trusted', () => { + vi.mocked(isWorkspaceTrusted).mockReturnValue(false); + (mockFsExistsSync as Mock).mockReturnValue(true); + const userSettingsContent = { + theme: 'dark', + sandbox: false, + contextFileName: 'USER.md', + }; + const workspaceSettingsContent = { + sandbox: true, + contextFileName: 'WORKSPACE.md', + }; + + (fs.readFileSync as Mock).mockImplementation( + (p: fs.PathOrFileDescriptor) => { + if (p === USER_SETTINGS_PATH) + return JSON.stringify(userSettingsContent); + if (p === MOCK_WORKSPACE_SETTINGS_PATH) + return JSON.stringify(workspaceSettingsContent); + return '{}'; + }, + ); + + const settings = loadSettings(MOCK_WORKSPACE_DIR); + + expect(settings.merged.sandbox).toBe(false); // User setting + expect(settings.merged.contextFileName).toBe('USER.md'); // User setting + expect(settings.merged.theme).toBe('dark'); // User setting + }); + }); }); diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts index 3df98d95..3f94fe65 100644 --- a/packages/cli/src/config/settings.ts +++ b/packages/cli/src/config/settings.ts @@ -16,6 +16,7 @@ import { import stripJsonComments from 'strip-json-comments'; import { DefaultLight } from '../ui/themes/default-light.js'; import { DefaultDark } from '../ui/themes/default.js'; +import { isWorkspaceTrusted } from './trustedFolders.js'; import { Settings, MemoryImportFormat } from './settingsSchema.js'; export type { Settings, MemoryImportFormat }; @@ -73,34 +74,37 @@ function mergeSettings( system: Settings, user: Settings, workspace: Settings, + isTrusted: boolean, ): Settings { + const safeWorkspace = isTrusted ? workspace : ({} as Settings); + // folderTrust is not supported at workspace level. // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { folderTrust, ...workspaceWithoutFolderTrust } = workspace; + const { folderTrust, ...safeWorkspaceWithoutFolderTrust } = safeWorkspace; return { ...user, - ...workspaceWithoutFolderTrust, + ...safeWorkspaceWithoutFolderTrust, ...system, customThemes: { ...(user.customThemes || {}), - ...(workspace.customThemes || {}), + ...(safeWorkspace.customThemes || {}), ...(system.customThemes || {}), }, mcpServers: { ...(user.mcpServers || {}), - ...(workspace.mcpServers || {}), + ...(safeWorkspace.mcpServers || {}), ...(system.mcpServers || {}), }, includeDirectories: [ ...(system.includeDirectories || []), ...(user.includeDirectories || []), - ...(workspace.includeDirectories || []), + ...(safeWorkspace.includeDirectories || []), ], chatCompression: { ...(system.chatCompression || {}), ...(user.chatCompression || {}), - ...(workspace.chatCompression || {}), + ...(safeWorkspace.chatCompression || {}), }, }; } @@ -111,11 +115,13 @@ export class LoadedSettings { user: SettingsFile, workspace: SettingsFile, errors: SettingsError[], + isTrusted: boolean, ) { this.system = system; this.user = user; this.workspace = workspace; this.errors = errors; + this.isTrusted = isTrusted; this._merged = this.computeMergedSettings(); } @@ -123,6 +129,7 @@ export class LoadedSettings { readonly user: SettingsFile; readonly workspace: SettingsFile; readonly errors: SettingsError[]; + readonly isTrusted: boolean; private _merged: Settings; @@ -135,6 +142,7 @@ export class LoadedSettings { this.system.settings, this.user.settings, this.workspace.settings, + this.isTrusted, ); } @@ -403,11 +411,16 @@ export function loadSettings(workspaceDir: string): LoadedSettings { } } + // For the initial trust check, we can only use user and system settings. + const initialTrustCheckSettings = { ...systemSettings, ...userSettings }; + const isTrusted = isWorkspaceTrusted(initialTrustCheckSettings) ?? true; + // Create a temporary merged settings object to pass to loadEnvironment. const tempMergedSettings = mergeSettings( systemSettings, userSettings, workspaceSettings, + isTrusted, ); // loadEnviroment depends on settings so we have to create a temp version of @@ -434,6 +447,7 @@ export function loadSettings(workspaceDir: string): LoadedSettings { settings: workspaceSettings, }, settingsErrors, + isTrusted, ); // Validate chatCompression settings |
