summaryrefslogtreecommitdiff
path: root/packages/cli/src/config
diff options
context:
space:
mode:
authorshrutip90 <[email protected]>2025-08-21 00:38:12 -0700
committerGitHub <[email protected]>2025-08-21 07:38:12 +0000
commitba5309c4050efde8b0be0d9dd726e5c5f1a4c4c6 (patch)
treed34cc814b2968bc8a09add14128544afe0f4f529 /packages/cli/src/config
parent0242ecd83a5b0673a6ef97a406a743b286ef12e2 (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.ts64
-rw-r--r--packages/cli/src/config/settings.ts26
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