mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-02 00:09:30 +01:00
* Rename sandbox setting to chat.agent.sandbox (#303421) Rename the top-level sandbox setting from `chat.tools.terminal.sandbox.enabled` to `chat.agent.sandbox` to reflect that sandboxing is a general agent concept, not terminal-specific. - Update setting ID value to `chat.agent.sandbox` - Update description to be more general - Deprecate old `chat.tools.terminal.sandbox.enabled` setting - Update telemetry event name Fixes #303421 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * updating terminal sandbox to agent sandbox --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -15,8 +15,8 @@ const enum ShellIntegrationTimeoutOverride {
|
||||
const isWindows = process.platform === 'win32';
|
||||
const isMacOS = process.platform === 'darwin';
|
||||
const sandboxFileSystemSetting = isMacOS
|
||||
? 'chat.tools.terminal.sandbox.macFileSystem'
|
||||
: 'chat.tools.terminal.sandbox.linuxFileSystem';
|
||||
? 'chat.agent.sandboxFileSystem.mac'
|
||||
: 'chat.agent.sandboxFileSystem.linux';
|
||||
|
||||
/**
|
||||
* Extracts all text content from a LanguageModelToolResult.
|
||||
@@ -297,13 +297,13 @@ function extractTextContent(result: vscode.LanguageModelToolResult): string {
|
||||
(isWindows ? suite.skip : suite)('sandbox on', () => {
|
||||
|
||||
setup(async () => {
|
||||
const sandboxConfig = vscode.workspace.getConfiguration('chat.tools.terminal.sandbox');
|
||||
await sandboxConfig.update('enabled', true, vscode.ConfigurationTarget.Global);
|
||||
const configuration = vscode.workspace.getConfiguration();
|
||||
await configuration.update('chat.agent.sandbox', true, vscode.ConfigurationTarget.Global);
|
||||
});
|
||||
|
||||
teardown(async () => {
|
||||
const sandboxConfig = vscode.workspace.getConfiguration('chat.tools.terminal.sandbox');
|
||||
await sandboxConfig.update('enabled', undefined, vscode.ConfigurationTarget.Global);
|
||||
const configuration = vscode.workspace.getConfiguration();
|
||||
await configuration.update('chat.agent.sandbox', undefined, vscode.ConfigurationTarget.Global);
|
||||
});
|
||||
|
||||
test('echo works in sandbox and output is clean', async function () {
|
||||
@@ -326,7 +326,7 @@ function extractTextContent(result: vscode.LanguageModelToolResult): string {
|
||||
const acceptable = [
|
||||
[
|
||||
'Command failed while running in sandboxed mode. If the command failed due to sandboxing:',
|
||||
`- If it would be reasonable to extend the sandbox rules, work with the user to update allowWrite for file system access problems in ${sandboxFileSystemSetting}, or to add required domains to chat.tools.terminal.sandbox.network.allowedDomains.`,
|
||||
`- If it would be reasonable to extend the sandbox rules, work with the user to update allowWrite for file system access problems in ${sandboxFileSystemSetting}, or to add required domains to chat.agent.sandboxNetwork.allowedDomains.`,
|
||||
'- Otherwise, immediately retry the command with requestUnsandboxedExecution=true. Do NOT ask the user \u2014 setting this flag automatically shows a confirmation prompt to the user.',
|
||||
'',
|
||||
'Here is the output of the command:',
|
||||
@@ -375,7 +375,7 @@ function extractTextContent(result: vscode.LanguageModelToolResult): string {
|
||||
? `/bin/bash: /tmp/${marker}.txt: Operation not permitted`
|
||||
: `/usr/bin/bash: line 1: /tmp/${marker}.txt: Read-only file system`;
|
||||
const sandboxBody = [
|
||||
`- If it would be reasonable to extend the sandbox rules, work with the user to update allowWrite for file system access problems in ${sandboxFileSystemSetting}, or to add required domains to chat.tools.terminal.sandbox.network.allowedDomains.`,
|
||||
`- If it would be reasonable to extend the sandbox rules, work with the user to update allowWrite for file system access problems in ${sandboxFileSystemSetting}, or to add required domains to chat.agent.sandboxNetwork.allowedDomains.`,
|
||||
'- Otherwise, immediately retry the command with requestUnsandboxedExecution=true. Do NOT ask the user \u2014 setting this flag automatically shows a confirmation prompt to the user.',
|
||||
'',
|
||||
'Here is the output of the command:',
|
||||
|
||||
@@ -444,13 +444,13 @@ class ConfigurationTelemetryContribution extends Disposable implements IWorkbenc
|
||||
source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'source of the setting' };
|
||||
}>('terminal.integrated.suggest.enabled', { settingValue: this.getValueToReport(key, target), source });
|
||||
return;
|
||||
case TerminalContribSettingId.TerminalSandboxEnabled:
|
||||
case TerminalContribSettingId.AgentSandboxEnabled:
|
||||
this.telemetryService.publicLog2<UpdatedSettingEvent, {
|
||||
owner: 'isidorn';
|
||||
comment: 'This is used to know if terminal sandbox is enabled or not';
|
||||
comment: 'This is used to know if agent sandbox is enabled or not';
|
||||
settingValue: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'value of the setting' };
|
||||
source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'source of the setting' };
|
||||
}>('chat.tools.terminal.sandbox.enabled', { settingValue: this.getValueToReport(key, target), source });
|
||||
}>('chat.agent.sandbox', { settingValue: this.getValueToReport(key, target), source });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -728,19 +728,67 @@ Registry.as<IConfigurationMigrationRegistry>(WorkbenchExtensions.ConfigurationMi
|
||||
|
||||
Registry.as<IConfigurationMigrationRegistry>(WorkbenchExtensions.ConfigurationMigration)
|
||||
.registerConfigurationMigrations([{
|
||||
key: TerminalContribSettingId.DeprecatedTerminalSandboxNetwork,
|
||||
migrateFn: (value: { allowedDomains?: string[]; deniedDomains?: string[]; allowTrustedDomains?: boolean }, valueAccessor) => {
|
||||
key: TerminalContribSettingId.DeprecatedTerminalSandboxEnabled,
|
||||
migrateFn: (value: boolean, valueAccessor) => {
|
||||
const configurationKeyValuePairs: ConfigurationKeyValuePairs = [];
|
||||
if (value?.allowedDomains !== undefined && valueAccessor(TerminalContribSettingId.TerminalSandboxNetworkAllowedDomains) === undefined) {
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.TerminalSandboxNetworkAllowedDomains, { value: value.allowedDomains }]);
|
||||
if (value !== undefined && valueAccessor(TerminalContribSettingId.AgentSandboxEnabled) === undefined) {
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.AgentSandboxEnabled, { value }]);
|
||||
}
|
||||
if (value?.deniedDomains !== undefined && valueAccessor(TerminalContribSettingId.TerminalSandboxNetworkDeniedDomains) === undefined) {
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.TerminalSandboxNetworkDeniedDomains, { value: value.deniedDomains }]);
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.DeprecatedTerminalSandboxEnabled, { value: undefined }]);
|
||||
return configurationKeyValuePairs;
|
||||
}
|
||||
}, {
|
||||
key: TerminalContribSettingId.DeprecatedTerminalSandboxNetwork,
|
||||
migrateFn: (value: { allowedDomains?: string[]; deniedDomains?: string[] }, valueAccessor) => {
|
||||
const configurationKeyValuePairs: ConfigurationKeyValuePairs = [];
|
||||
if (value?.allowedDomains !== undefined && valueAccessor(TerminalContribSettingId.AgentSandboxNetworkAllowedDomains) === undefined) {
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.AgentSandboxNetworkAllowedDomains, { value: value.allowedDomains }]);
|
||||
}
|
||||
if (value?.allowTrustedDomains !== undefined && valueAccessor(TerminalContribSettingId.TerminalSandboxNetworkAllowTrustedDomains) === undefined) {
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.TerminalSandboxNetworkAllowTrustedDomains, { value: value.allowTrustedDomains }]);
|
||||
if (value?.deniedDomains !== undefined && valueAccessor(TerminalContribSettingId.AgentSandboxNetworkDeniedDomains) === undefined) {
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.AgentSandboxNetworkDeniedDomains, { value: value.deniedDomains }]);
|
||||
}
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.DeprecatedTerminalSandboxNetwork, { value: undefined }]);
|
||||
return configurationKeyValuePairs;
|
||||
}
|
||||
}, {
|
||||
key: TerminalContribSettingId.DeprecatedTerminalSandboxNetworkAllowedDomains,
|
||||
migrateFn: (value: string[], valueAccessor) => {
|
||||
const configurationKeyValuePairs: ConfigurationKeyValuePairs = [];
|
||||
if (value !== undefined && valueAccessor(TerminalContribSettingId.AgentSandboxNetworkAllowedDomains) === undefined) {
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.AgentSandboxNetworkAllowedDomains, { value }]);
|
||||
}
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.DeprecatedTerminalSandboxNetworkAllowedDomains, { value: undefined }]);
|
||||
return configurationKeyValuePairs;
|
||||
}
|
||||
}, {
|
||||
key: TerminalContribSettingId.DeprecatedTerminalSandboxNetworkDeniedDomains,
|
||||
migrateFn: (value: string[], valueAccessor) => {
|
||||
const configurationKeyValuePairs: ConfigurationKeyValuePairs = [];
|
||||
if (value !== undefined && valueAccessor(TerminalContribSettingId.AgentSandboxNetworkDeniedDomains) === undefined) {
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.AgentSandboxNetworkDeniedDomains, { value }]);
|
||||
}
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.DeprecatedTerminalSandboxNetworkDeniedDomains, { value: undefined }]);
|
||||
return configurationKeyValuePairs;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: TerminalContribSettingId.DeprecatedTerminalSandboxLinuxFileSystem,
|
||||
migrateFn: (value: { denyRead?: string[]; allowWrite?: string[]; denyWrite?: string[] }, valueAccessor) => {
|
||||
const configurationKeyValuePairs: ConfigurationKeyValuePairs = [];
|
||||
if (value !== undefined && valueAccessor(TerminalContribSettingId.AgentSandboxLinuxFileSystem) === undefined) {
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.AgentSandboxLinuxFileSystem, { value }]);
|
||||
}
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.DeprecatedTerminalSandboxLinuxFileSystem, { value: undefined }]);
|
||||
return configurationKeyValuePairs;
|
||||
}
|
||||
}, {
|
||||
key: TerminalContribSettingId.DeprecatedTerminalSandboxMacFileSystem,
|
||||
migrateFn: (value: { denyRead?: string[]; allowWrite?: string[]; denyWrite?: string[] }, valueAccessor) => {
|
||||
const configurationKeyValuePairs: ConfigurationKeyValuePairs = [];
|
||||
if (value !== undefined && valueAccessor(TerminalContribSettingId.AgentSandboxMacFileSystem) === undefined) {
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.AgentSandboxMacFileSystem, { value }]);
|
||||
}
|
||||
configurationKeyValuePairs.push([TerminalContribSettingId.DeprecatedTerminalSandboxMacFileSystem, { value: undefined }]);
|
||||
return configurationKeyValuePairs;
|
||||
}
|
||||
}]);
|
||||
|
||||
@@ -46,11 +46,17 @@ export const enum TerminalContribSettingId {
|
||||
EnableAutoApprove = TerminalChatAgentToolsSettingId.EnableAutoApprove,
|
||||
ShellIntegrationTimeout = TerminalChatAgentToolsSettingId.ShellIntegrationTimeout,
|
||||
OutputLocation = TerminalChatAgentToolsSettingId.OutputLocation,
|
||||
TerminalSandboxEnabled = TerminalChatAgentToolsSettingId.TerminalSandboxEnabled,
|
||||
AgentSandboxEnabled = TerminalChatAgentToolsSettingId.AgentSandboxEnabled,
|
||||
DeprecatedTerminalSandboxNetwork = TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetwork,
|
||||
TerminalSandboxNetworkAllowedDomains = TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains,
|
||||
TerminalSandboxNetworkDeniedDomains = TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains,
|
||||
TerminalSandboxNetworkAllowTrustedDomains = TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains,
|
||||
DeprecatedTerminalSandboxEnabled = TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxEnabled,
|
||||
DeprecatedTerminalSandboxNetworkAllowedDomains = TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkAllowedDomains,
|
||||
DeprecatedTerminalSandboxNetworkDeniedDomains = TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkDeniedDomains,
|
||||
DeprecatedTerminalSandboxLinuxFileSystem = TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxLinuxFileSystem,
|
||||
DeprecatedTerminalSandboxMacFileSystem = TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxMacFileSystem,
|
||||
AgentSandboxNetworkAllowedDomains = TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains,
|
||||
AgentSandboxNetworkDeniedDomains = TerminalChatAgentToolsSettingId.AgentSandboxNetworkDeniedDomains,
|
||||
AgentSandboxLinuxFileSystem = TerminalChatAgentToolsSettingId.AgentSandboxLinuxFileSystem,
|
||||
AgentSandboxMacFileSystem = TerminalChatAgentToolsSettingId.AgentSandboxMacFileSystem,
|
||||
}
|
||||
|
||||
// HACK: Export some context key strings from `terminalContrib/` that are depended upon elsewhere.
|
||||
|
||||
@@ -31,7 +31,6 @@ import { CreateAndRunTaskTool, CreateAndRunTaskToolData } from './tools/task/cre
|
||||
import { GetTaskOutputTool, GetTaskOutputToolData } from './tools/task/getTaskOutputTool.js';
|
||||
import { RunTaskTool, RunTaskToolData } from './tools/task/runTaskTool.js';
|
||||
import { InstantiationType, registerSingleton } from '../../../../../platform/instantiation/common/extensions.js';
|
||||
import { ITrustedDomainService } from '../../../url/common/trustedDomainService.js';
|
||||
import { ITerminalSandboxService, TerminalSandboxService } from '../common/terminalSandboxService.js';
|
||||
import { isNumber } from '../../../../../base/common/types.js';
|
||||
|
||||
@@ -87,7 +86,6 @@ export class ChatAgentToolsContribution extends Disposable implements IWorkbench
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ILanguageModelToolsService private readonly _toolsService: ILanguageModelToolsService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ITrustedDomainService private readonly _trustedDomainService: ITrustedDomainService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -141,17 +139,16 @@ export class ChatAgentToolsContribution extends Disposable implements IWorkbench
|
||||
// sandbox state.
|
||||
this._register(this._configurationService.onDidChangeConfiguration(e => {
|
||||
if (
|
||||
e.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxEnabled) ||
|
||||
e.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains) ||
|
||||
e.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains) ||
|
||||
e.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains)
|
||||
e.affectsConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxEnabled) ||
|
||||
e.affectsConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains) ||
|
||||
e.affectsConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxNetworkDeniedDomains) ||
|
||||
e.affectsConfiguration(TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxEnabled) ||
|
||||
e.affectsConfiguration(TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkAllowedDomains) ||
|
||||
e.affectsConfiguration(TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkDeniedDomains)
|
||||
) {
|
||||
this._registerRunInTerminalTool();
|
||||
}
|
||||
}));
|
||||
this._register(this._trustedDomainService.onDidChangeTrustedDomains(() => {
|
||||
this._registerRunInTerminalTool();
|
||||
}));
|
||||
}
|
||||
|
||||
private _runInTerminalTool: RunInTerminalTool | undefined;
|
||||
|
||||
@@ -30,14 +30,14 @@ export class SandboxOutputAnalyzer extends Disposable implements IOutputAnalyzer
|
||||
|
||||
const os = await this._sandboxService.getOS();
|
||||
const fileSystemSetting = os === OperatingSystem.Linux
|
||||
? TerminalChatAgentToolsSettingId.TerminalSandboxLinuxFileSystem
|
||||
: TerminalChatAgentToolsSettingId.TerminalSandboxMacFileSystem;
|
||||
? TerminalChatAgentToolsSettingId.AgentSandboxLinuxFileSystem
|
||||
: TerminalChatAgentToolsSettingId.AgentSandboxMacFileSystem;
|
||||
|
||||
const prefix = knownFailure
|
||||
? 'Command failed while running in sandboxed mode. If the command failed due to sandboxing:'
|
||||
: 'Command ran in sandboxed mode and may have been blocked by the sandbox. If the command failed due to sandboxing:';
|
||||
return `${prefix}
|
||||
- If it would be reasonable to extend the sandbox rules, work with the user to update allowWrite for file system access problems in ${fileSystemSetting}, or to add required domains to ${TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains}.
|
||||
- If it would be reasonable to extend the sandbox rules, work with the user to update allowWrite for file system access problems in ${fileSystemSetting}, or to add required domains to ${TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains}.
|
||||
- Otherwise, immediately retry the command with requestUnsandboxedExecution=true. Do NOT ask the user — setting this flag automatically shows a confirmation prompt to the user.
|
||||
|
||||
Here is the output of the command:\n`;
|
||||
|
||||
@@ -20,12 +20,11 @@ export const enum TerminalChatAgentToolsSettingId {
|
||||
ShellIntegrationTimeout = 'chat.tools.terminal.shellIntegrationTimeout',
|
||||
AutoReplyToPrompts = 'chat.tools.terminal.autoReplyToPrompts',
|
||||
OutputLocation = 'chat.tools.terminal.outputLocation',
|
||||
TerminalSandboxEnabled = 'chat.tools.terminal.sandbox.enabled',
|
||||
TerminalSandboxNetworkAllowedDomains = 'chat.tools.terminal.sandbox.network.allowedDomains',
|
||||
TerminalSandboxNetworkDeniedDomains = 'chat.tools.terminal.sandbox.network.deniedDomains',
|
||||
TerminalSandboxNetworkAllowTrustedDomains = 'chat.tools.terminal.sandbox.network.allowTrustedDomains',
|
||||
TerminalSandboxLinuxFileSystem = 'chat.tools.terminal.sandbox.linuxFileSystem',
|
||||
TerminalSandboxMacFileSystem = 'chat.tools.terminal.sandbox.macFileSystem',
|
||||
AgentSandboxEnabled = 'chat.agent.sandbox',
|
||||
AgentSandboxNetworkAllowedDomains = 'chat.agent.sandboxNetwork.allowedDomains',
|
||||
AgentSandboxNetworkDeniedDomains = 'chat.agent.sandboxNetwork.deniedDomains',
|
||||
AgentSandboxLinuxFileSystem = 'chat.agent.sandboxFileSystem.linux',
|
||||
AgentSandboxMacFileSystem = 'chat.agent.sandboxFileSystem.mac',
|
||||
PreventShellHistory = 'chat.tools.terminal.preventShellHistory',
|
||||
EnforceTimeoutFromModel = 'chat.tools.terminal.enforceTimeoutFromModel',
|
||||
IdlePollInterval = 'chat.tools.terminal.idlePollInterval',
|
||||
@@ -34,7 +33,12 @@ export const enum TerminalChatAgentToolsSettingId {
|
||||
TerminalProfileMacOs = 'chat.tools.terminal.terminalProfile.osx',
|
||||
TerminalProfileWindows = 'chat.tools.terminal.terminalProfile.windows',
|
||||
|
||||
DeprecatedTerminalSandboxEnabled = 'chat.tools.terminal.sandbox.enabled',
|
||||
DeprecatedTerminalSandboxNetwork = 'chat.tools.terminal.sandbox.network',
|
||||
DeprecatedTerminalSandboxNetworkAllowedDomains = 'chat.tools.terminal.sandbox.network.allowedDomains',
|
||||
DeprecatedTerminalSandboxNetworkDeniedDomains = 'chat.tools.terminal.sandbox.network.deniedDomains',
|
||||
DeprecatedTerminalSandboxLinuxFileSystem = 'chat.tools.terminal.sandbox.linuxFileSystem',
|
||||
DeprecatedTerminalSandboxMacFileSystem = 'chat.tools.terminal.sandbox.macFileSystem',
|
||||
DeprecatedAutoApproveCompatible = 'chat.agent.terminal.autoApprove',
|
||||
DeprecatedAutoApprove1 = 'chat.agent.terminal.allowList',
|
||||
DeprecatedAutoApprove2 = 'chat.agent.terminal.denyList',
|
||||
@@ -524,8 +528,8 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary<IConfigurati
|
||||
mode: 'auto'
|
||||
}
|
||||
},
|
||||
[TerminalChatAgentToolsSettingId.TerminalSandboxEnabled]: {
|
||||
markdownDescription: localize('terminalSandbox.enabledSetting', "Controls whether to run commands in a sandboxed terminal for the run in terminal tool."),
|
||||
[TerminalChatAgentToolsSettingId.AgentSandboxEnabled]: {
|
||||
markdownDescription: localize('agentSandbox.enabledSetting', "Controls whether agent mode uses sandboxing to restrict what tools can do. When enabled, tools like the terminal are run in a sandboxed environment to limit access to the system."),
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
tags: ['preview'],
|
||||
@@ -534,48 +538,41 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary<IConfigurati
|
||||
mode: 'auto'
|
||||
}
|
||||
},
|
||||
[TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains]: {
|
||||
markdownDescription: localize('terminalSandbox.networkSetting.allowedDomains', "Note: this setting is applicable only when {0} is enabled. Allowed domains for network access in the terminal sandbox. Supports wildcards like {1} and an empty list means no network access.", `\`#${TerminalChatAgentToolsSettingId.TerminalSandboxEnabled}#\``, '`*.example.com`'),
|
||||
[TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains]: {
|
||||
markdownDescription: localize('agentSandbox.networkSetting.allowedDomains', "Note: this setting is applicable only when {0} is enabled. Allowed domains for network access in sandbox. Supports wildcards like {1} and an empty list means no network access.", `\`#${TerminalChatAgentToolsSettingId.AgentSandboxEnabled}#\``, '`*.example.com`'),
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
default: [],
|
||||
tags: ['preview'],
|
||||
restricted: true,
|
||||
},
|
||||
[TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains]: {
|
||||
markdownDescription: localize('terminalSandbox.networkSetting.deniedDomains', "Note: this setting is applicable only when {0} is enabled. Array of denied domains for network access in the terminal sandbox (checked first, takes precedence over {1}).", `\`#${TerminalChatAgentToolsSettingId.TerminalSandboxEnabled}#\``, `\`#${TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains}#\``),
|
||||
[TerminalChatAgentToolsSettingId.AgentSandboxNetworkDeniedDomains]: {
|
||||
markdownDescription: localize('agentSandbox.networkSetting.deniedDomains', "Note: this setting is applicable only when {0} is enabled. Array of denied domains for network access in sandbox (checked first, takes precedence over {1}).", `\`#${TerminalChatAgentToolsSettingId.AgentSandboxEnabled}#\``, `\`#${TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains}#\``),
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
default: [],
|
||||
tags: ['preview'],
|
||||
restricted: true,
|
||||
},
|
||||
[TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains]: {
|
||||
markdownDescription: localize('terminalSandbox.networkSetting.allowTrustedDomains', "Note: this setting is applicable only when {0} is enabled. When enabled, the Trusted Domains list is included in the allowed domains for network access in the terminal sandbox.", `\`#${TerminalChatAgentToolsSettingId.TerminalSandboxEnabled}#\``),
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
tags: ['preview'],
|
||||
restricted: true,
|
||||
},
|
||||
[TerminalChatAgentToolsSettingId.TerminalSandboxLinuxFileSystem]: {
|
||||
markdownDescription: localize('terminalSandbox.linuxFileSystemSetting', "Note: this setting is applicable only when {0} is enabled. Controls file system access in the terminal sandbox on Linux. Paths do not support glob patterns, only literal paths (ex: ./src/, ~/.ssh, .env). **bubblewrap** and **socat** should be installed for this setting to work.", `\`#${TerminalChatAgentToolsSettingId.TerminalSandboxEnabled}#\``),
|
||||
[TerminalChatAgentToolsSettingId.AgentSandboxLinuxFileSystem]: {
|
||||
markdownDescription: localize('agentSandbox.linuxFileSystemSetting', "Note: this setting is applicable only when {0} is enabled. Controls file system access in sandbox on Linux. Paths do not support glob patterns, only literal paths (ex: ./src/, ~/.ssh, .env). **bubblewrap** and **socat** should be installed for this setting to work.", `\`#${TerminalChatAgentToolsSettingId.AgentSandboxEnabled}#\``),
|
||||
type: 'object',
|
||||
properties: {
|
||||
denyRead: {
|
||||
type: 'array',
|
||||
description: localize('terminalSandbox.linuxFileSystemSetting.denyRead', "Array of paths to deny read access. Leave empty to allow reading all paths."),
|
||||
description: localize('agentSandbox.linuxFileSystemSetting.denyRead', "Array of paths to deny read access. Leave empty to allow reading all paths."),
|
||||
items: { type: 'string' },
|
||||
default: []
|
||||
},
|
||||
allowWrite: {
|
||||
type: 'array',
|
||||
description: localize('terminalSandbox.linuxFileSystemSetting.allowWrite', "Array of paths to allow write access. Leave empty to disallow all writes."),
|
||||
description: localize('agentSandbox.linuxFileSystemSetting.allowWrite', "Array of paths to allow write access. Leave empty to disallow all writes."),
|
||||
items: { type: 'string' },
|
||||
default: ['.']
|
||||
},
|
||||
denyWrite: {
|
||||
type: 'array',
|
||||
description: localize('terminalSandbox.linuxFileSystemSetting.denyWrite', "Array of paths to deny write access within allowed paths (takes precedence over allowWrite)."),
|
||||
description: localize('agentSandbox.linuxFileSystemSetting.denyWrite', "Array of paths to deny write access within allowed paths (takes precedence over allowWrite)."),
|
||||
items: { type: 'string' },
|
||||
default: []
|
||||
}
|
||||
@@ -588,25 +585,25 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary<IConfigurati
|
||||
tags: ['preview'],
|
||||
restricted: true,
|
||||
},
|
||||
[TerminalChatAgentToolsSettingId.TerminalSandboxMacFileSystem]: {
|
||||
markdownDescription: localize('terminalSandbox.macFileSystemSetting', "Note: this setting is applicable only when {0} is enabled. Controls file system access in the terminal sandbox on macOS. Paths also support git-style glob patterns(ex: *.ts, ./src, ./src/**/*.ts, file?.txt).", `\`#${TerminalChatAgentToolsSettingId.TerminalSandboxEnabled}#\``),
|
||||
[TerminalChatAgentToolsSettingId.AgentSandboxMacFileSystem]: {
|
||||
markdownDescription: localize('agentSandbox.macFileSystemSetting', "Note: this setting is applicable only when {0} is enabled. Controls file system access in sandbox on macOS. Paths also support git-style glob patterns(ex: *.ts, ./src, ./src/**/*.ts, file?.txt).", `\`#${TerminalChatAgentToolsSettingId.AgentSandboxEnabled}#\``),
|
||||
type: 'object',
|
||||
properties: {
|
||||
denyRead: {
|
||||
type: 'array',
|
||||
description: localize('terminalSandbox.macFileSystemSetting.denyRead', "Array of paths to deny read access. Leave empty to allow reading all paths."),
|
||||
description: localize('agentSandbox.macFileSystemSetting.denyRead', "Array of paths to deny read access. Leave empty to allow reading all paths."),
|
||||
items: { type: 'string' },
|
||||
default: []
|
||||
},
|
||||
allowWrite: {
|
||||
type: 'array',
|
||||
description: localize('terminalSandbox.macFileSystemSetting.allowWrite', "Array of paths to allow write access. Leave empty to disallow all writes."),
|
||||
description: localize('agentSandbox.macFileSystemSetting.allowWrite', "Array of paths to allow write access. Leave empty to disallow all writes."),
|
||||
items: { type: 'string' },
|
||||
default: ['.']
|
||||
},
|
||||
denyWrite: {
|
||||
type: 'array',
|
||||
description: localize('terminalSandbox.macFileSystemSetting.denyWrite', "Array of paths to deny write access within allowed paths (takes precedence over allowWrite)."),
|
||||
description: localize('agentSandbox.macFileSystemSetting.denyWrite', "Array of paths to deny write access within allowed paths (takes precedence over allowWrite)."),
|
||||
items: { type: 'string' },
|
||||
default: []
|
||||
}
|
||||
@@ -659,10 +656,41 @@ terminalChatAgentToolsConfiguration[TerminalChatAgentToolsSettingId.DeprecatedTe
|
||||
type: 'object',
|
||||
deprecated: true,
|
||||
markdownDeprecationMessage: localize(
|
||||
'terminalSandboxNetwork.deprecated',
|
||||
'This setting has been split into {0}, {1}, and {2}.',
|
||||
`\`#${TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains}#\``,
|
||||
`\`#${TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains}#\``,
|
||||
`\`#${TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains}#\``,
|
||||
'agentSandboxNetwork.deprecated',
|
||||
'This setting has been split into {0} and {1}.',
|
||||
`\`#${TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains}#\``,
|
||||
`\`#${TerminalChatAgentToolsSettingId.AgentSandboxNetworkDeniedDomains}#\``,
|
||||
),
|
||||
};
|
||||
|
||||
terminalChatAgentToolsConfiguration[TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxEnabled] = {
|
||||
type: 'boolean',
|
||||
deprecated: true,
|
||||
markdownDeprecationMessage: localize('agentSandboxEnabled.deprecated', 'Use {0} instead', `\`#${TerminalChatAgentToolsSettingId.AgentSandboxEnabled}#\``),
|
||||
};
|
||||
|
||||
terminalChatAgentToolsConfiguration[TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkAllowedDomains] = {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
deprecated: true,
|
||||
markdownDeprecationMessage: localize('agentSandboxNetworkAllowedDomains.deprecated', 'Use {0} instead', `\`#${TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains}#\``),
|
||||
};
|
||||
|
||||
terminalChatAgentToolsConfiguration[TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkDeniedDomains] = {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
deprecated: true,
|
||||
markdownDeprecationMessage: localize('agentSandboxNetworkDeniedDomains.deprecated', 'Use {0} instead', `\`#${TerminalChatAgentToolsSettingId.AgentSandboxNetworkDeniedDomains}#\``),
|
||||
};
|
||||
|
||||
terminalChatAgentToolsConfiguration[TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxLinuxFileSystem] = {
|
||||
type: 'object',
|
||||
deprecated: true,
|
||||
markdownDeprecationMessage: localize('agentSandboxLinuxFileSystem.deprecated', 'Use {0} instead', `\`#${TerminalChatAgentToolsSettingId.AgentSandboxLinuxFileSystem}#\``),
|
||||
};
|
||||
|
||||
terminalChatAgentToolsConfiguration[TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxMacFileSystem] = {
|
||||
type: 'object',
|
||||
deprecated: true,
|
||||
markdownDeprecationMessage: localize('agentSandboxMacFileSystem.deprecated', 'Use {0} instead', `\`#${TerminalChatAgentToolsSettingId.AgentSandboxMacFileSystem}#\``),
|
||||
};
|
||||
|
||||
@@ -22,7 +22,6 @@ import { ILogService } from '../../../../../platform/log/common/log.js';
|
||||
import { IRemoteAgentService } from '../../../../services/remote/common/remoteAgentService.js';
|
||||
import { TerminalChatAgentToolsSettingId } from './terminalChatAgentToolsConfiguration.js';
|
||||
import { IRemoteAgentEnvironment } from '../../../../../platform/remote/common/remoteAgentEnvironment.js';
|
||||
import { ITrustedDomainService } from '../../../url/common/trustedDomainService.js';
|
||||
import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js';
|
||||
import { IProductService } from '../../../../../platform/product/common/productService.js';
|
||||
import { ILifecycleService, WillShutdownJoinerOrder } from '../../../../services/lifecycle/common/lifecycle.js';
|
||||
@@ -132,7 +131,6 @@ export class TerminalSandboxService extends Disposable implements ITerminalSandb
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
|
||||
@ITrustedDomainService private readonly _trustedDomainService: ITrustedDomainService,
|
||||
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
|
||||
@IProductService private readonly _productService: IProductService,
|
||||
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
|
||||
@@ -150,21 +148,21 @@ export class TerminalSandboxService extends Disposable implements ITerminalSandb
|
||||
this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, (e: IConfigurationChangeEvent | undefined) => {
|
||||
// If terminal sandbox settings changed, update sandbox config.
|
||||
if (
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxEnabled) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxLinuxFileSystem) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxMacFileSystem)
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxEnabled) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxNetworkDeniedDomains) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxLinuxFileSystem) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxMacFileSystem) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxEnabled) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkAllowedDomains) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkDeniedDomains) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxLinuxFileSystem) ||
|
||||
e?.affectsConfiguration(TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxMacFileSystem)
|
||||
) {
|
||||
this.setNeedsForceUpdateConfigFile();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this._trustedDomainService.onDidChangeTrustedDomains(() => {
|
||||
this.setNeedsForceUpdateConfigFile();
|
||||
}));
|
||||
|
||||
this._register(this._workspaceContextService.onDidChangeWorkspaceFolders(() => {
|
||||
this.setNeedsForceUpdateConfigFile();
|
||||
}));
|
||||
@@ -428,7 +426,7 @@ export class TerminalSandboxService extends Disposable implements ITerminalSandb
|
||||
if (os === OperatingSystem.Windows) {
|
||||
return false;
|
||||
}
|
||||
return this._configurationService.getValue<boolean>(TerminalChatAgentToolsSettingId.TerminalSandboxEnabled);
|
||||
return this._getSettingValue<boolean>(TerminalChatAgentToolsSettingId.AgentSandboxEnabled, TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxEnabled) ?? false;
|
||||
}
|
||||
|
||||
private async _resolveSrtPath(): Promise<void> {
|
||||
@@ -451,27 +449,21 @@ export class TerminalSandboxService extends Disposable implements ITerminalSandb
|
||||
await this._initTempDir();
|
||||
}
|
||||
if (this._tempDir) {
|
||||
const allowedDomainsSetting = this._configurationService.getValue<string[]>(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains) ?? [];
|
||||
const deniedDomainsSetting = this._configurationService.getValue<string[]>(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains) ?? [];
|
||||
const allowTrustedDomains = this._configurationService.getValue<boolean>(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains) ?? false;
|
||||
const allowedDomainsSetting = this._getSettingValue<string[]>(TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains, TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkAllowedDomains) ?? [];
|
||||
const deniedDomainsSetting = this._getSettingValue<string[]>(TerminalChatAgentToolsSettingId.AgentSandboxNetworkDeniedDomains, TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkDeniedDomains) ?? [];
|
||||
const linuxFileSystemSetting = this._os === OperatingSystem.Linux
|
||||
? this._configurationService.getValue<{ denyRead?: string[]; allowWrite?: string[]; denyWrite?: string[] }>(TerminalChatAgentToolsSettingId.TerminalSandboxLinuxFileSystem) ?? {}
|
||||
? this._getSettingValue<{ denyRead?: string[]; allowWrite?: string[]; denyWrite?: string[] }>(TerminalChatAgentToolsSettingId.AgentSandboxLinuxFileSystem, TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxLinuxFileSystem) ?? {}
|
||||
: {};
|
||||
const macFileSystemSetting = this._os === OperatingSystem.Macintosh
|
||||
? this._configurationService.getValue<{ denyRead?: string[]; allowWrite?: string[]; denyWrite?: string[] }>(TerminalChatAgentToolsSettingId.TerminalSandboxMacFileSystem) ?? {}
|
||||
? this._getSettingValue<{ denyRead?: string[]; allowWrite?: string[]; denyWrite?: string[] }>(TerminalChatAgentToolsSettingId.AgentSandboxMacFileSystem, TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxMacFileSystem) ?? {}
|
||||
: {};
|
||||
const configFileUri = URI.joinPath(this._tempDir, `vscode-sandbox-settings-${this._sandboxSettingsId}.json`);
|
||||
const linuxAllowWrite = this._updateAllowWritePathsWithWorkspaceFolders(linuxFileSystemSetting.allowWrite);
|
||||
const macAllowWrite = this._updateAllowWritePathsWithWorkspaceFolders(macFileSystemSetting.allowWrite);
|
||||
|
||||
let allowedDomains = allowedDomainsSetting;
|
||||
if (allowTrustedDomains) {
|
||||
allowedDomains = this._addTrustedDomainsToAllowedDomains(allowedDomains);
|
||||
}
|
||||
|
||||
const sandboxSettings = {
|
||||
network: {
|
||||
allowedDomains,
|
||||
allowedDomains: allowedDomainsSetting,
|
||||
deniedDomains: deniedDomainsSetting
|
||||
},
|
||||
filesystem: {
|
||||
@@ -542,33 +534,14 @@ export class TerminalSandboxService extends Disposable implements ITerminalSandb
|
||||
}
|
||||
|
||||
public getResolvedNetworkDomains(): ITerminalSandboxResolvedNetworkDomains {
|
||||
let allowedDomains = this._configurationService.getValue<string[]>(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains) ?? [];
|
||||
const deniedDomains = this._configurationService.getValue<string[]>(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains) ?? [];
|
||||
const allowTrustedDomains = this._configurationService.getValue<boolean>(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains) ?? false;
|
||||
if (allowTrustedDomains) {
|
||||
allowedDomains = this._addTrustedDomainsToAllowedDomains(allowedDomains);
|
||||
}
|
||||
const allowedDomains = this._getSettingValue<string[]>(TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains, TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkAllowedDomains) ?? [];
|
||||
const deniedDomains = this._getSettingValue<string[]>(TerminalChatAgentToolsSettingId.AgentSandboxNetworkDeniedDomains, TerminalChatAgentToolsSettingId.DeprecatedTerminalSandboxNetworkDeniedDomains) ?? [];
|
||||
return {
|
||||
allowedDomains,
|
||||
deniedDomains
|
||||
};
|
||||
}
|
||||
|
||||
private _addTrustedDomainsToAllowedDomains(allowedDomains: string[]): string[] {
|
||||
const allowedDomainsSet = new Set(allowedDomains);
|
||||
for (const domain of this._trustedDomainService.trustedDomains) {
|
||||
try {
|
||||
const uri = new URL(domain);
|
||||
allowedDomainsSet.add(uri.hostname);
|
||||
} catch {
|
||||
if (domain !== '*') {
|
||||
allowedDomainsSet.add(domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Array.from(allowedDomainsSet);
|
||||
}
|
||||
|
||||
private _updateAllowWritePathsWithWorkspaceFolders(configuredAllowWrite: string[] | undefined): string[] {
|
||||
const workspaceFolderPaths = this._workspaceContextService.getWorkspace().folders.map(folder => folder.uri.path);
|
||||
return [...new Set([...workspaceFolderPaths, ...this._defaultWritePaths, ...(configuredAllowWrite ?? [])])];
|
||||
@@ -590,4 +563,16 @@ export class TerminalSandboxService extends Disposable implements ITerminalSandb
|
||||
return this._sandboxHelperService.checkSandboxDependencies();
|
||||
}
|
||||
|
||||
|
||||
private _getSettingValue<T>(settingId: TerminalChatAgentToolsSettingId, deprecatedSettingId?: TerminalChatAgentToolsSettingId): T | undefined {
|
||||
const setting = this._configurationService.inspect<T>(settingId);
|
||||
const deprecatedSetting = deprecatedSettingId ? this._configurationService.inspect<T>(deprecatedSettingId) : undefined;
|
||||
|
||||
if (setting.userValue === undefined && deprecatedSetting?.userValue !== undefined) {
|
||||
this._logService.warn(`TerminalSandboxService: Using deprecated setting ${deprecatedSettingId} because ${settingId} is not set. Please update your settings to use ${settingId} instead.`);
|
||||
return deprecatedSetting.value;
|
||||
}
|
||||
return setting.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { strictEqual, ok } from 'assert';
|
||||
import { deepStrictEqual, strictEqual, ok } from 'assert';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js';
|
||||
import { TestInstantiationService } from '../../../../../../platform/instantiation/test/common/instantiationServiceMock.js';
|
||||
import { TestLifecycleService, workbenchInstantiationService } from '../../../../../test/browser/workbenchTestServices.js';
|
||||
@@ -15,7 +15,6 @@ import { IEnvironmentService } from '../../../../../../platform/environment/comm
|
||||
import { ILogService, NullLogService } from '../../../../../../platform/log/common/log.js';
|
||||
import { IProductService } from '../../../../../../platform/product/common/productService.js';
|
||||
import { IRemoteAgentService } from '../../../../../services/remote/common/remoteAgentService.js';
|
||||
import { ITrustedDomainService } from '../../../../url/common/trustedDomainService.js';
|
||||
import { URI } from '../../../../../../base/common/uri.js';
|
||||
import { TerminalChatAgentToolsSettingId } from '../../common/terminalChatAgentToolsConfiguration.js';
|
||||
import { Event, Emitter } from '../../../../../../base/common/event.js';
|
||||
@@ -28,12 +27,11 @@ import { testWorkspace } from '../../../../../../platform/workspace/test/common/
|
||||
import { ILifecycleService } from '../../../../../services/lifecycle/common/lifecycle.js';
|
||||
import { ISandboxDependencyStatus, ISandboxHelperService } from '../../../../../../platform/sandbox/common/sandboxHelperService.js';
|
||||
|
||||
suite('TerminalSandboxService - allowTrustedDomains', () => {
|
||||
suite('TerminalSandboxService - network domains', () => {
|
||||
const store = ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
let instantiationService: TestInstantiationService;
|
||||
let configurationService: TestConfigurationService;
|
||||
let trustedDomainService: MockTrustedDomainService;
|
||||
let fileService: MockFileService;
|
||||
let lifecycleService: TestLifecycleService;
|
||||
let workspaceContextService: MockWorkspaceContextService;
|
||||
@@ -44,16 +42,6 @@ suite('TerminalSandboxService - allowTrustedDomains', () => {
|
||||
let deletedFolders: string[];
|
||||
const windowId = 7;
|
||||
|
||||
class MockTrustedDomainService implements ITrustedDomainService {
|
||||
_serviceBrand: undefined;
|
||||
private _onDidChangeTrustedDomains = new Emitter<void>();
|
||||
readonly onDidChangeTrustedDomains: Event<void> = this._onDidChangeTrustedDomains.event;
|
||||
trustedDomains: string[] = [];
|
||||
isValid(_resource: URI): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class MockFileService {
|
||||
async createFile(uri: URI, content: VSBuffer): Promise<any> {
|
||||
const contentString = content.toString();
|
||||
@@ -174,7 +162,6 @@ suite('TerminalSandboxService - allowTrustedDomains', () => {
|
||||
deletedFolders = [];
|
||||
instantiationService = workbenchInstantiationService({}, store);
|
||||
configurationService = new TestConfigurationService();
|
||||
trustedDomainService = new MockTrustedDomainService();
|
||||
fileService = new MockFileService();
|
||||
lifecycleService = store.add(new TestLifecycleService());
|
||||
workspaceContextService = new MockWorkspaceContextService();
|
||||
@@ -187,10 +174,9 @@ suite('TerminalSandboxService - allowTrustedDomains', () => {
|
||||
workspaceContextService.setWorkspaceFolders([URI.file('/workspace-one')]);
|
||||
|
||||
// Setup default configuration
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxEnabled, true);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains, false);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxEnabled, true);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxNetworkDeniedDomains, []);
|
||||
|
||||
instantiationService.stub(IConfigurationService, configurationService);
|
||||
instantiationService.stub(IFileService, fileService);
|
||||
@@ -203,7 +189,6 @@ suite('TerminalSandboxService - allowTrustedDomains', () => {
|
||||
instantiationService.stub(ILogService, new NullLogService());
|
||||
instantiationService.stub(IProductService, productService);
|
||||
instantiationService.stub(IRemoteAgentService, new MockRemoteAgentService());
|
||||
instantiationService.stub(ITrustedDomainService, trustedDomainService);
|
||||
instantiationService.stub(IWorkspaceContextService, workspaceContextService);
|
||||
instantiationService.stub(ILifecycleService, lifecycleService);
|
||||
instantiationService.stub(ISandboxHelperService, sandboxHelperService);
|
||||
@@ -243,14 +228,16 @@ suite('TerminalSandboxService - allowTrustedDomains', () => {
|
||||
ok(result.sandboxConfigPath, 'Sandbox config path should be returned when prereqs pass');
|
||||
});
|
||||
|
||||
test('should filter out sole wildcard (*) from trusted domains', async () => {
|
||||
// Setup: Enable allowTrustedDomains and add * to trusted domains
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains, true);
|
||||
trustedDomainService.trustedDomains = ['*'];
|
||||
test('should preserve configured network domains', async () => {
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains, ['example.com', '*.github.com']);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxNetworkDeniedDomains, ['blocked.example.com']);
|
||||
|
||||
const sandboxService = store.add(instantiationService.createInstance(TerminalSandboxService));
|
||||
deepStrictEqual(sandboxService.getResolvedNetworkDomains(), {
|
||||
allowedDomains: ['example.com', '*.github.com'],
|
||||
deniedDomains: ['blocked.example.com']
|
||||
});
|
||||
|
||||
const configPath = await sandboxService.getSandboxConfigPath();
|
||||
|
||||
ok(configPath, 'Config path should be defined');
|
||||
@@ -258,128 +245,14 @@ suite('TerminalSandboxService - allowTrustedDomains', () => {
|
||||
ok(configContent, 'Config file should be created');
|
||||
|
||||
const config = JSON.parse(configContent);
|
||||
strictEqual(config.network.allowedDomains.length, 0, 'Sole wildcard * should be filtered out');
|
||||
});
|
||||
|
||||
test('should allow wildcards with domains like *.github.com', async () => {
|
||||
// Setup: Enable allowTrustedDomains and add *.github.com
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains, true);
|
||||
trustedDomainService.trustedDomains = ['*.github.com'];
|
||||
|
||||
const sandboxService = store.add(instantiationService.createInstance(TerminalSandboxService));
|
||||
const configPath = await sandboxService.getSandboxConfigPath();
|
||||
|
||||
ok(configPath, 'Config path should be defined');
|
||||
const configContent = createdFiles.get(configPath);
|
||||
ok(configContent, 'Config file should be created');
|
||||
|
||||
const config = JSON.parse(configContent);
|
||||
strictEqual(config.network.allowedDomains.length, 1, 'Wildcard domain should be included');
|
||||
strictEqual(config.network.allowedDomains[0], '*.github.com', 'Wildcard domain should match');
|
||||
});
|
||||
|
||||
test('should combine trusted domains with configured allowedDomains, filtering out *', async () => {
|
||||
// Setup: Enable allowTrustedDomains with multiple domains including *
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains, ['example.com']);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains, true);
|
||||
trustedDomainService.trustedDomains = ['*', '*.github.com', 'microsoft.com'];
|
||||
|
||||
const sandboxService = store.add(instantiationService.createInstance(TerminalSandboxService));
|
||||
const configPath = await sandboxService.getSandboxConfigPath();
|
||||
|
||||
ok(configPath, 'Config path should be defined');
|
||||
const configContent = createdFiles.get(configPath);
|
||||
ok(configContent, 'Config file should be created');
|
||||
|
||||
const config = JSON.parse(configContent);
|
||||
strictEqual(config.network.allowedDomains.length, 3, 'Should have 3 domains (excluding *)');
|
||||
ok(config.network.allowedDomains.includes('example.com'), 'Should include configured domain');
|
||||
ok(config.network.allowedDomains.includes('*.github.com'), 'Should include wildcard domain');
|
||||
ok(config.network.allowedDomains.includes('microsoft.com'), 'Should include microsoft.com');
|
||||
ok(!config.network.allowedDomains.includes('*'), 'Should not include sole wildcard');
|
||||
});
|
||||
|
||||
test('should not include trusted domains when allowTrustedDomains is false', async () => {
|
||||
// Setup: Disable allowTrustedDomains
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains, ['example.com']);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains, false);
|
||||
trustedDomainService.trustedDomains = ['*', '*.github.com'];
|
||||
|
||||
const sandboxService = store.add(instantiationService.createInstance(TerminalSandboxService));
|
||||
const configPath = await sandboxService.getSandboxConfigPath();
|
||||
|
||||
ok(configPath, 'Config path should be defined');
|
||||
const configContent = createdFiles.get(configPath);
|
||||
ok(configContent, 'Config file should be created');
|
||||
|
||||
const config = JSON.parse(configContent);
|
||||
strictEqual(config.network.allowedDomains.length, 1, 'Should only have configured domain');
|
||||
strictEqual(config.network.allowedDomains[0], 'example.com', 'Should only include example.com');
|
||||
});
|
||||
|
||||
test('should deduplicate domains when combining sources', async () => {
|
||||
// Setup: Same domain in both sources
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains, ['github.com', '*.github.com']);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains, true);
|
||||
trustedDomainService.trustedDomains = ['*.github.com', 'github.com'];
|
||||
|
||||
const sandboxService = store.add(instantiationService.createInstance(TerminalSandboxService));
|
||||
const configPath = await sandboxService.getSandboxConfigPath();
|
||||
|
||||
ok(configPath, 'Config path should be defined');
|
||||
const configContent = createdFiles.get(configPath);
|
||||
ok(configContent, 'Config file should be created');
|
||||
|
||||
const config = JSON.parse(configContent);
|
||||
strictEqual(config.network.allowedDomains.length, 2, 'Should have 2 unique domains');
|
||||
ok(config.network.allowedDomains.includes('github.com'), 'Should include github.com');
|
||||
ok(config.network.allowedDomains.includes('*.github.com'), 'Should include *.github.com');
|
||||
});
|
||||
|
||||
test('should handle empty trusted domains list', async () => {
|
||||
// Setup: Empty trusted domains
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains, ['example.com']);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains, true);
|
||||
trustedDomainService.trustedDomains = [];
|
||||
|
||||
const sandboxService = store.add(instantiationService.createInstance(TerminalSandboxService));
|
||||
const configPath = await sandboxService.getSandboxConfigPath();
|
||||
|
||||
ok(configPath, 'Config path should be defined');
|
||||
const configContent = createdFiles.get(configPath);
|
||||
ok(configContent, 'Config file should be created');
|
||||
|
||||
const config = JSON.parse(configContent);
|
||||
strictEqual(config.network.allowedDomains.length, 1, 'Should have only configured domain');
|
||||
strictEqual(config.network.allowedDomains[0], 'example.com', 'Should only include example.com');
|
||||
});
|
||||
|
||||
test('should handle only * in trusted domains', async () => {
|
||||
// Setup: Only * in trusted domains (edge case)
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkDeniedDomains, []);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowTrustedDomains, true);
|
||||
trustedDomainService.trustedDomains = ['*'];
|
||||
|
||||
const sandboxService = store.add(instantiationService.createInstance(TerminalSandboxService));
|
||||
const configPath = await sandboxService.getSandboxConfigPath();
|
||||
|
||||
ok(configPath, 'Config path should be defined');
|
||||
const configContent = createdFiles.get(configPath);
|
||||
ok(configContent, 'Config file should be created');
|
||||
|
||||
const config = JSON.parse(configContent);
|
||||
strictEqual(config.network.allowedDomains.length, 0, 'Should have no domains (* filtered out)');
|
||||
deepStrictEqual(config.network, {
|
||||
allowedDomains: ['example.com', '*.github.com'],
|
||||
deniedDomains: ['blocked.example.com']
|
||||
});
|
||||
});
|
||||
|
||||
test('should refresh allowWrite paths when workspace folders change', async () => {
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxLinuxFileSystem, {
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxLinuxFileSystem, {
|
||||
allowWrite: ['/configured/path'],
|
||||
denyRead: [],
|
||||
denyWrite: []
|
||||
|
||||
@@ -48,7 +48,6 @@ import type { IMarkdownString } from '../../../../../../base/common/htmlContent.
|
||||
import { IAgentSessionsService } from '../../../../chat/browser/agentSessions/agentSessionsService.js';
|
||||
import { IAgentSession } from '../../../../chat/browser/agentSessions/agentSessionsModel.js';
|
||||
import { isDisposable, toDisposable } from '../../../../../../base/common/lifecycle.js';
|
||||
import { ITrustedDomainService } from '../../../../url/common/trustedDomainService.js';
|
||||
import { ChatAgentToolsContribution } from '../../browser/terminal.chatAgentTools.contribution.js';
|
||||
import { TerminalToolId } from '../../browser/tools/toolIds.js';
|
||||
import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js';
|
||||
@@ -1987,13 +1986,11 @@ suite('ChatAgentToolsContribution - tool registration refresh', () => {
|
||||
let instantiationService: TestInstantiationService;
|
||||
let configurationService: TestConfigurationService;
|
||||
let registeredToolData: Map<string, IToolData>;
|
||||
let trustedDomainsEmitter: Emitter<void>;
|
||||
let sandboxEnabled: boolean;
|
||||
|
||||
setup(() => {
|
||||
configurationService = new TestConfigurationService();
|
||||
registeredToolData = new Map();
|
||||
trustedDomainsEmitter = store.add(new Emitter<void>());
|
||||
sandboxEnabled = false;
|
||||
|
||||
const logService = new NullLogService();
|
||||
@@ -2052,13 +2049,6 @@ suite('ChatAgentToolsContribution - tool registration refresh', () => {
|
||||
getDefaultProfile: async () => ({ path: 'bash' } as ITerminalProfile)
|
||||
});
|
||||
|
||||
instantiationService.stub(ITrustedDomainService, {
|
||||
_serviceBrand: undefined,
|
||||
onDidChangeTrustedDomains: trustedDomainsEmitter.event,
|
||||
isValid: () => true,
|
||||
trustedDomains: [],
|
||||
});
|
||||
|
||||
const contextKeyService = instantiationService.get(IContextKeyService);
|
||||
const registeredToolImpls = new Map<string, IToolImpl>();
|
||||
const mockToolsService: Partial<ILanguageModelToolsService> = {
|
||||
@@ -2120,10 +2110,10 @@ suite('ChatAgentToolsContribution - tool registration refresh', () => {
|
||||
|
||||
// Enable sandbox and fire config change
|
||||
sandboxEnabled = true;
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.TerminalSandboxEnabled, true);
|
||||
configurationService.setUserConfiguration(TerminalChatAgentToolsSettingId.AgentSandboxEnabled, true);
|
||||
configurationService.onDidChangeConfigurationEmitter.fire({
|
||||
affectsConfiguration: (key: string) => key === TerminalChatAgentToolsSettingId.TerminalSandboxEnabled,
|
||||
affectedKeys: new Set([TerminalChatAgentToolsSettingId.TerminalSandboxEnabled]),
|
||||
affectsConfiguration: (key: string) => key === TerminalChatAgentToolsSettingId.AgentSandboxEnabled,
|
||||
affectedKeys: new Set([TerminalChatAgentToolsSettingId.AgentSandboxEnabled]),
|
||||
source: ConfigurationTarget.USER,
|
||||
change: null!,
|
||||
});
|
||||
@@ -2137,23 +2127,6 @@ suite('ChatAgentToolsContribution - tool registration refresh', () => {
|
||||
ok(propertiesAfter?.['requestUnsandboxedExecution'], 'Expected requestUnsandboxedExecution after enabling sandbox');
|
||||
});
|
||||
|
||||
test('should refresh run_in_terminal tool data when trusted domains change', async () => {
|
||||
await createContribution();
|
||||
|
||||
const toolDataBefore = registeredToolData.get(TerminalToolId.RunInTerminal);
|
||||
ok(toolDataBefore, 'Expected run_in_terminal tool to be registered');
|
||||
|
||||
// Fire trusted domains change
|
||||
trustedDomainsEmitter.fire();
|
||||
|
||||
// Wait for async registration
|
||||
await flushAsync();
|
||||
|
||||
// Tool should still be registered (re-registered with fresh data)
|
||||
const toolDataAfter = registeredToolData.get(TerminalToolId.RunInTerminal);
|
||||
ok(toolDataAfter, 'Expected run_in_terminal tool to still be registered after trusted domains change');
|
||||
});
|
||||
|
||||
test('should refresh run_in_terminal tool data when sandbox network setting changes', async () => {
|
||||
sandboxEnabled = true;
|
||||
await createContribution();
|
||||
@@ -2163,8 +2136,8 @@ suite('ChatAgentToolsContribution - tool registration refresh', () => {
|
||||
|
||||
// Fire network config change
|
||||
configurationService.onDidChangeConfigurationEmitter.fire({
|
||||
affectsConfiguration: (key: string) => key === TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains,
|
||||
affectedKeys: new Set([TerminalChatAgentToolsSettingId.TerminalSandboxNetworkAllowedDomains]),
|
||||
affectsConfiguration: (key: string) => key === TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains,
|
||||
affectedKeys: new Set([TerminalChatAgentToolsSettingId.AgentSandboxNetworkAllowedDomains]),
|
||||
source: ConfigurationTarget.USER,
|
||||
change: null!,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user