Extension workspace trust request using product/settings files (#121021)

* Extension workspace trust request using product/settings files
This commit is contained in:
Ladislau Szomoru 2021-04-13 08:17:48 +02:00 committed by GitHub
parent 601dec633b
commit 0c555419fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 328 additions and 28 deletions

View file

@ -33,3 +33,28 @@ export function provideInstalledExtensionProposals(existing: string[], additiona
return undefined;
}
export function provideWorkspaceTrustExtensionProposals(existing: string[], range: vscode.Range): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
if (Array.isArray(existing)) {
const extensions = vscode.extensions.all.filter(e => e.packageJSON.main);
const extensionProposals = extensions.filter(e => existing.indexOf(e.id) === -1);
if (extensionProposals.length) {
return extensionProposals.map(e => {
const item = new vscode.CompletionItem(e.id);
const insertText = `"${e.id}": {\n\t"request": "onStart",\n\t"version": "${e.packageJSON.version}"\n}`;
item.kind = vscode.CompletionItemKind.Value;
item.insertText = insertText;
item.range = range;
item.filterText = insertText;
return item;
});
} else {
const example = new vscode.CompletionItem(localize('exampleExtension', "Example"));
example.insertText = '"vscode.csharp: {\n\t"request": "onStart",\n\t"version": "0.0.0"\n}`;"';
example.kind = vscode.CompletionItemKind.Value;
example.range = range;
return [example];
}
}
return undefined;
}

View file

@ -6,7 +6,7 @@
import * as vscode from 'vscode';
import { getLocation, Location, parse } from 'jsonc-parser';
import * as nls from 'vscode-nls';
import { provideInstalledExtensionProposals } from './extensionsProposals';
import { provideInstalledExtensionProposals, provideWorkspaceTrustExtensionProposals } from './extensionsProposals';
const localize = nls.loadMessageBundle();
@ -60,6 +60,15 @@ export class SettingsDocument {
return provideInstalledExtensionProposals(alreadyConfigured, `: [\n\t"ui"\n]`, range, true);
}
// security.workspace.trust.extensionRequest
if (location.path[0] === 'security.workspace.trust.extensionRequest' && location.path.length === 2 && location.isAtPropertyKey) {
let alreadyConfigured: string[] = [];
try {
alreadyConfigured = Object.keys(parse(this.document.getText())['security.workspace.trust.extensionRequest']);
} catch (e) {/* ignore error */ }
return provideWorkspaceTrustExtensionProposals(alreadyConfigured, range);
}
return this.provideLanguageOverridesCompletionItems(location, position);
}

View file

@ -20,6 +20,11 @@ export type ConfigurationSyncStore = {
authenticationProviders: IStringDictionary<{ scopes: string[] }>
};
export type ExtensionWorkspaceTrustRequest = {
readonly default?: 'never' | 'onStart' | 'onDemand',
readonly override?: 'never' | 'onStart' | 'onDemand'
};
export interface IProductConfiguration {
readonly version: string;
readonly date?: string;
@ -116,6 +121,7 @@ export interface IProductConfiguration {
readonly extensionKind?: { readonly [extensionId: string]: ('ui' | 'workspace' | 'web')[]; };
readonly extensionSyncedKeys?: { readonly [extensionId: string]: string[]; };
readonly extensionAllowedProposedApi?: readonly string[];
readonly extensionWorkspaceTrustRequest?: { readonly [extensionId: string]: ExtensionWorkspaceTrustRequest };
readonly msftInternalDomains?: string[];
readonly linkProtectionTrustedDomains?: readonly string[];

View file

@ -307,18 +307,6 @@ export function isAuthenticaionProviderExtension(manifest: IExtensionManifest):
return manifest.contributes && manifest.contributes.authentication ? manifest.contributes.authentication.length > 0 : false;
}
export function getExtensionWorkspaceTrustRequestType(manifest: IExtensionManifest): ExtensionWorkspaceTrustRequestType {
if (manifest.workspaceTrust?.request !== undefined) {
return manifest.workspaceTrust.request;
}
if (!manifest.main) {
return 'never';
}
return 'onStart';
}
export interface IScannedExtension {
readonly identifier: IExtensionIdentifier;
readonly location: URI;

View file

@ -20,12 +20,13 @@ import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteB
import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { getExtensionWorkspaceTrustRequestType, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
import { isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { foreground, listActiveSelectionForeground, listActiveSelectionBackground, listInactiveSelectionForeground, listInactiveSelectionBackground, listFocusForeground, listFocusBackground, listHoverForeground, listHoverBackground } from 'vs/platform/theme/common/colorRegistry';
import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { localize } from 'vs/nls';
import { IExtensionWorkspaceTrustRequestService } from 'vs/workbench/services/extensions/common/extensionWorkspaceTrustRequest';
export const EXTENSION_LIST_ELEMENT_HEIGHT = 62;
@ -66,6 +67,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
@IExtensionService private readonly extensionService: IExtensionService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionWorkspaceTrustRequestService private readonly extensionWorkspaceTrustRequestService: IExtensionWorkspaceTrustRequestService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
) { }
@ -205,7 +207,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
if (extension.local?.manifest.workspaceTrust?.request) {
const trustRequirement = extension.local.manifest.workspaceTrust;
const requestType = getExtensionWorkspaceTrustRequestType(extension.local.manifest);
const requestType = this.extensionWorkspaceTrustRequestService.getExtensionWorkspaceTrustRequestType(extension.local.manifest);
if (requestType !== 'never' && trustRequirement.request !== 'never') {
data.workspaceTrustDescription.textContent = trustRequirement.description;
} else if (requestType === 'onStart') {

View file

@ -37,7 +37,7 @@ import { alert } from 'vs/base/browser/ui/aria/aria';
import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IAction, Action, Separator, ActionRunner } from 'vs/base/common/actions';
import { ExtensionIdentifier, getExtensionWorkspaceTrustRequestType, IExtensionDescription, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { IProductService } from 'vs/platform/product/common/productService';
import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon';
@ -48,6 +48,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IExtensionWorkspaceTrustRequestService } from 'vs/workbench/services/extensions/common/extensionWorkspaceTrustRequest';
// Extensions that are automatically classified as Programming Language extensions, but should be Feature extensions
const FORCE_FEATURE_EXTENSIONS = ['vscode.git', 'vscode.search-result'];
@ -118,6 +119,7 @@ export class ExtensionsListView extends ViewPane {
@IWorkspaceContextService protected contextService: IWorkspaceContextService,
@IExperimentService private readonly experimentService: IExperimentService,
@IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService,
@IExtensionWorkspaceTrustRequestService private readonly extensionWorkspaceTrustRequestService: IExtensionWorkspaceTrustRequestService,
@IWorkbenchExtensionManagementService protected readonly extensionManagementService: IWorkbenchExtensionManagementService,
@IProductService protected readonly productService: IProductService,
@IContextKeyService contextKeyService: IContextKeyService,
@ -559,15 +561,15 @@ export class ExtensionsListView extends ViewPane {
value = value.replace(/@trustRequired/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase();
const result = local.filter(extension => extension.local && getExtensionWorkspaceTrustRequestType(extension.local.manifest) !== 'never' && (extension.name.toLowerCase().indexOf(value) > -1 || extension.displayName.toLowerCase().indexOf(value) > -1));
const result = local.filter(extension => extension.local && this.extensionWorkspaceTrustRequestService.getExtensionWorkspaceTrustRequestType(extension.local.manifest) !== 'never' && (extension.name.toLowerCase().indexOf(value) > -1 || extension.displayName.toLowerCase().indexOf(value) > -1));
if (onStartOnly) {
const onStartExtensions = result.filter(extension => extension.local && getExtensionWorkspaceTrustRequestType(extension.local.manifest) === 'onStart');
const onStartExtensions = result.filter(extension => extension.local && this.extensionWorkspaceTrustRequestService.getExtensionWorkspaceTrustRequestType(extension.local.manifest) === 'onStart');
return this.sortExtensions(onStartExtensions, options);
}
if (onDemandOnly) {
const onDemandExtensions = result.filter(extension => extension.local && getExtensionWorkspaceTrustRequestType(extension.local.manifest) === 'onDemand');
const onDemandExtensions = result.filter(extension => extension.local && this.extensionWorkspaceTrustRequestService.getExtensionWorkspaceTrustRequestType(extension.local.manifest) === 'onDemand');
return this.sortExtensions(onDemandExtensions, options);
}

View file

@ -28,7 +28,7 @@ import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarA
import { IEditorRegistry, Extensions as EditorExtensions, EditorDescriptor } from 'vs/workbench/browser/editor';
import { WorkspaceTrustEditor } from 'vs/workbench/contrib/workspace/browser/workspaceTrustEditor';
import { WorkspaceTrustEditorInput } from 'vs/workbench/services/workspaces/browser/workspaceTrustEditorInput';
import { WorkspaceTrustContext, WORKSPACE_TRUST_ENABLED } from 'vs/workbench/services/workspaces/common/workspaceTrust';
import { WorkspaceTrustContext, WORKSPACE_TRUST_ENABLED, WORKSPACE_TRUST_EXTENSION_REQUEST } from 'vs/workbench/services/workspaces/common/workspaceTrust';
import { EditorInput, Extensions as EditorInputExtensions, IEditorInputSerializer, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
@ -423,6 +423,31 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
default: false,
included: !isWeb,
description: localize('workspace.trust.description', "Controls whether or not workspace trust is enabled within VS Code."),
},
[WORKSPACE_TRUST_EXTENSION_REQUEST]: {
type: 'object',
markdownDescription: localize('security.workspace.trust.extensionRequest', "Override the workspace trust request of an extension. Extensions using `never` will always be enabled. Extensions using `onDemand` will always be enabled, and the extension will request for workspace trust only when necessary. Extensions using `onStart` will only be enabled only when the workspace is trusted."),
patternProperties: {
'([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$': {
type: 'object',
properties: {
'request': {
type: 'string',
enum: ['never', 'onDemand', 'onStart'],
enumDescriptions: [
localize('security.workspace.trust.extensionRequest.request.never', "Extension will always be enabled."),
localize('security.workspace.trust.extensionRequest.request.onDemand', "Extension will always be enabled, and the extension will request for workspace trust only when necessary."),
localize('security.workspace.trust.extensionRequest.request.onStart', "Extension will only be enabled only when the workspace is trusted."),
],
description: localize('security.workspace.trust.extensionRequest.request', "Defines the workspace trust request setting for the extension."),
},
'version': {
type: 'string',
description: localize('security.workspace.trust.extensionRequest.version', "Defines the version of the extension for which the override should be applied. If not specified, the override will be applied independent of the extension version."),
}
}
}
}
}
}
});

View file

@ -24,7 +24,7 @@ import { localize } from 'vs/nls';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { ExtensionWorkspaceTrustRequestType, getExtensionWorkspaceTrustRequestType } from 'vs/platform/extensions/common/extensions';
import { ExtensionWorkspaceTrustRequestType } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IPromptChoiceWithMenu } from 'vs/platform/notification/common/notification';
import { Link } from 'vs/platform/opener/browser/link';
@ -43,6 +43,7 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/com
import { getInstalledExtensions, IExtensionStatus } from 'vs/workbench/contrib/extensions/common/extensionsUtils';
import { trustedForegroundColor, untrustedForegroundColor } from 'vs/workbench/contrib/workspace/browser/workspaceTrustColors';
import { IWorkspaceTrustSettingChangeEvent, WorkspaceTrustSettingArrayRenderer, WorkspaceTrustTree, WorkspaceTrustTreeModel } from 'vs/workbench/contrib/workspace/browser/workspaceTrustTree';
import { IExtensionWorkspaceTrustRequestService } from 'vs/workbench/services/extensions/common/extensionWorkspaceTrustRequest';
import { WorkspaceTrustEditorInput } from 'vs/workbench/services/workspaces/browser/workspaceTrustEditorInput';
const untrustedIcon = registerCodicon('workspace-untrusted-icon', Codicon.workspaceUntrusted);
@ -80,6 +81,7 @@ export class WorkspaceTrustEditor extends EditorPane {
@IStorageService storageService: IStorageService,
@IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService,
@IExtensionWorkspaceTrustRequestService private readonly extensionWorkspaceTrustRequestService: IExtensionWorkspaceTrustRequestService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IDialogService private readonly dialogService: IDialogService,
@ -227,7 +229,7 @@ export class WorkspaceTrustEditor extends EditorPane {
}
private getExtensionCountByTrustRequestType(extensions: IExtensionStatus[], trustRequestType: ExtensionWorkspaceTrustRequestType): number {
const filtered = extensions.filter(ext => getExtensionWorkspaceTrustRequestType(ext.local.manifest) === trustRequestType);
const filtered = extensions.filter(ext => this.extensionWorkspaceTrustRequestService.getExtensionWorkspaceTrustRequestType(ext.local.manifest) === trustRequestType);
const set = new Set<string>();
for (const ext of filtered) {
set.add(ext.identifier.id);

View file

@ -12,7 +12,7 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { getExtensionWorkspaceTrustRequestType, IExtension, isAuthenticaionProviderExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
import { IExtension, isAuthenticaionProviderExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ExtensionKindController } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
@ -27,6 +27,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect';
import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService, WorkspaceTrustState, WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust';
import { Promises } from 'vs/base/common/async';
import { IExtensionWorkspaceTrustRequestService } from 'vs/workbench/services/extensions/common/extensionWorkspaceTrustRequest';
const SOURCE = 'IWorkbenchExtensionEnablementService';
@ -48,6 +49,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IExtensionWorkspaceTrustRequestService private readonly extensionWorkspaceTrustRequestService: IExtensionWorkspaceTrustRequestService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IProductService productService: IProductService,
@ -277,7 +279,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
private _isDisabledByTrustRequirement(extension: IExtension): boolean {
const workspaceTrustState = this.workspaceTrustManagementService.getWorkspaceTrustState();
if (getExtensionWorkspaceTrustRequestType(extension.manifest) === 'onStart') {
if (this.extensionWorkspaceTrustRequestService.getExtensionWorkspaceTrustRequestType(extension.manifest) === 'onStart') {
if (workspaceTrustState !== WorkspaceTrustState.Trusted) {
this._addToWorkspaceDisabledExtensionsByTrustRequirement(extension);
}

View file

@ -8,7 +8,7 @@ import {
ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService, InstallOptions, UninstallOptions, INSTALL_ERROR_NOT_SUPPORTED
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementServer, IExtensionManagementServerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionType, isLanguagePackExtension, IExtensionManifest, getExtensionWorkspaceTrustRequestType } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, isLanguagePackExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { Disposable } from 'vs/base/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@ -26,6 +26,7 @@ import { canceled } from 'vs/base/common/errors';
import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { Promises } from 'vs/base/common/async';
import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
import { IExtensionWorkspaceTrustRequestService } from 'vs/workbench/services/extensions/common/extensionWorkspaceTrustRequest';
export class ExtensionManagementService extends Disposable implements IWorkbenchExtensionManagementService {
@ -43,6 +44,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
constructor(
@IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService,
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
@IExtensionWorkspaceTrustRequestService private readonly extensionWorkspaceTrustRequestService: IExtensionWorkspaceTrustRequestService,
@IConfigurationService protected readonly configurationService: IConfigurationService,
@IProductService protected readonly productService: IProductService,
@IDownloadService protected readonly downloadService: IDownloadService,
@ -361,7 +363,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
}
protected async checkForWorkspaceTrust(manifest: IExtensionManifest): Promise<void> {
if (getExtensionWorkspaceTrustRequestType(manifest) === 'onStart') {
if (this.extensionWorkspaceTrustRequestService.getExtensionWorkspaceTrustRequestType(manifest) === 'onStart') {
const trustState = await this.workspaceTrustRequestService.requestWorkspaceTrust({
modal: true,
message: localize('extensionInstallWorkspaceTrustMessage', "Enabling this extension requires a trusted workspace."),

View file

@ -18,6 +18,7 @@ import { joinPath } from 'vs/base/common/resources';
import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
import { IExtensionWorkspaceTrustRequestService } from 'vs/workbench/services/extensions/common/extensionWorkspaceTrustRequest';
export class ExtensionManagementService extends BaseExtensionManagementService {
@ -25,6 +26,7 @@ export class ExtensionManagementService extends BaseExtensionManagementService {
@INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService,
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,
@IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
@IExtensionWorkspaceTrustRequestService extensionWorkspaceTrustRequestService: IExtensionWorkspaceTrustRequestService,
@IConfigurationService configurationService: IConfigurationService,
@IProductService productService: IProductService,
@IDownloadService downloadService: IDownloadService,
@ -33,7 +35,7 @@ export class ExtensionManagementService extends BaseExtensionManagementService {
@IDialogService dialogService: IDialogService,
@IWorkspaceTrustRequestService workspaceTrustRequestService: IWorkspaceTrustRequestService
) {
super(extensionManagementServerService, extensionGalleryService, configurationService, productService, downloadService, userDataAutoSyncEnablementService, userDataSyncResourceEnablementService, dialogService, workspaceTrustRequestService);
super(extensionManagementServerService, extensionGalleryService, extensionWorkspaceTrustRequestService, configurationService, productService, downloadService, userDataAutoSyncEnablementService, userDataSyncResourceEnablementService, dialogService, workspaceTrustRequestService);
}
protected async override installVSIX(vsix: URI, server: IExtensionManagementServer): Promise<ILocalExtension> {

View file

@ -12,7 +12,7 @@ import { Emitter } from 'vs/base/common/event';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage';
import { IExtensionContributions, ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions';
import { IExtensionContributions, ExtensionType, IExtension, IExtensionManifest, ExtensionWorkspaceTrustRequestType } from 'vs/platform/extensions/common/extensions';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@ -32,6 +32,7 @@ import { mock } from 'vs/base/test/common/mock';
import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect';
import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
import { TestWorkspaceTrustManagementService, TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService';
import { IExtensionWorkspaceTrustRequestService } from 'vs/workbench/services/extensions/common/extensionWorkspaceTrustRequest';
function createStorageService(instantiationService: TestInstantiationService): IStorageService {
let service = instantiationService.get(IStorageService);
@ -58,6 +59,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService {
instantiationService.get(IWorkspaceContextService),
instantiationService.get(IWorkbenchEnvironmentService) || instantiationService.stub(IWorkbenchEnvironmentService, { configuration: Object.create(null) } as IWorkbenchEnvironmentService),
extensionManagementService,
new class extends mock<IExtensionWorkspaceTrustRequestService>() { getExtensionWorkspaceTrustRequestType(manifest: IExtensionManifest): ExtensionWorkspaceTrustRequestType { return 'never'; } },
instantiationService.get(IConfigurationService),
extensionManagementServerService,
productService,

View file

@ -0,0 +1,106 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtensionWorkspaceTrustRequest } from 'vs/base/common/product';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionIdentifier, ExtensionWorkspaceTrustRequestType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IProductService } from 'vs/platform/product/common/productService';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { WORKSPACE_TRUST_EXTENSION_REQUEST } from 'vs/workbench/services/workspaces/common/workspaceTrust';
export const IExtensionWorkspaceTrustRequestService = createDecorator<IExtensionWorkspaceTrustRequestService>('extensionWorkspaceTrustRequestService');
export interface IExtensionWorkspaceTrustRequestService {
readonly _serviceBrand: undefined;
getExtensionWorkspaceTrustRequestType(manifest: IExtensionManifest): ExtensionWorkspaceTrustRequestType;
}
export class ExtensionWorkspaceTrustRequestService extends Disposable implements IExtensionWorkspaceTrustRequestService {
_serviceBrand: undefined;
private readonly _configuredExtensionWorkspaceTrustRequestMap: Map<string, { request: ExtensionWorkspaceTrustRequestType, version?: string }>;
private readonly _productExtensionWorkspaceTrustRequestMap: Map<string, ExtensionWorkspaceTrustRequest>;
constructor(
@IProductService productService: IProductService,
@IConfigurationService configurationService: IConfigurationService,
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService
) {
super();
// Settings.json
this._configuredExtensionWorkspaceTrustRequestMap = new Map<string, { request: ExtensionWorkspaceTrustRequestType, version?: string }>();
const configuredExtensionWorkspaceTrustRequests = configurationService.inspect<{ [key: string]: { request: ExtensionWorkspaceTrustRequestType, version?: string } }>(WORKSPACE_TRUST_EXTENSION_REQUEST).userValue || {};
for (const id of Object.keys(configuredExtensionWorkspaceTrustRequests)) {
this._configuredExtensionWorkspaceTrustRequestMap.set(ExtensionIdentifier.toKey(id), configuredExtensionWorkspaceTrustRequests[id]);
}
// Products.json
this._productExtensionWorkspaceTrustRequestMap = new Map<string, ExtensionWorkspaceTrustRequest>();
if (productService.extensionWorkspaceTrustRequest) {
for (const id of Object.keys(productService.extensionWorkspaceTrustRequest)) {
this._productExtensionWorkspaceTrustRequestMap.set(ExtensionIdentifier.toKey(id), productService.extensionWorkspaceTrustRequest[id]);
}
}
}
private getConfiguredExtensionWorkspaceTrustRequest(manifest: IExtensionManifest): ExtensionWorkspaceTrustRequestType | undefined {
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
const extensionWorkspaceTrustRequest = this._configuredExtensionWorkspaceTrustRequestMap.get(ExtensionIdentifier.toKey(extensionId));
if (extensionWorkspaceTrustRequest && (extensionWorkspaceTrustRequest.version === undefined || extensionWorkspaceTrustRequest.version === manifest.version)) {
return extensionWorkspaceTrustRequest.request;
}
return undefined;
}
private getProductExtensionWorkspaceTrustRequest(manifest: IExtensionManifest): ExtensionWorkspaceTrustRequest | undefined {
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
return this._productExtensionWorkspaceTrustRequestMap.get(ExtensionIdentifier.toKey(extensionId));
}
getExtensionWorkspaceTrustRequestType(manifest: IExtensionManifest): ExtensionWorkspaceTrustRequestType {
// Workspace trust feature is disabled, or extension has no entry point
if (!this._workspaceTrustManagementService.isWorkspaceTrustEnabled() || !manifest.main) {
return 'never';
}
// Get extension workspace trust requirements from settings.json
const configuredWorkspaceTrustRequest = this.getConfiguredExtensionWorkspaceTrustRequest(manifest);
// Get extension workspace trust requirements from product.json
const productWorkspaceTrustRequest = this.getProductExtensionWorkspaceTrustRequest(manifest);
// Use settings.json override value if it exists
if (configuredWorkspaceTrustRequest) {
return configuredWorkspaceTrustRequest;
}
// Use product.json override value if it exists
if (productWorkspaceTrustRequest?.override) {
return productWorkspaceTrustRequest.override;
}
// Use extension manifest value if it exists
if (manifest.workspaceTrust?.request !== undefined) {
return manifest.workspaceTrust.request;
}
// Use product.json default value if it exists
if (productWorkspaceTrustRequest?.default) {
return productWorkspaceTrustRequest.default;
}
return 'onStart';
}
}
registerSingleton(IExtensionWorkspaceTrustRequestService, ExtensionWorkspaceTrustRequestService);

View file

@ -0,0 +1,126 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { mock } from 'vs/base/test/common/mock';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { ExtensionWorkspaceTrustRequestService } from 'vs/workbench/services/extensions/common/extensionWorkspaceTrustRequest';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { WorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/common/workspaceTrust';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IProductService } from 'vs/platform/product/common/productService';
import { ExtensionWorkspaceTrustRequestType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
suite('ExtensionWorkspaceTrustRequestService', () => {
let testObject: ExtensionWorkspaceTrustRequestService;
let instantiationService: TestInstantiationService;
let testConfigurationService: TestConfigurationService;
setup(() => {
instantiationService = new TestInstantiationService();
testConfigurationService = new TestConfigurationService();
instantiationService.stub(IConfigurationService, testConfigurationService);
});
teardown(() => testObject.dispose());
function assertWorkspaceTrustRequest(extensionMaifest: IExtensionManifest, expected: ExtensionWorkspaceTrustRequestType): void {
testObject = instantiationService.createInstance(ExtensionWorkspaceTrustRequestService);
const workspaceTrustRequest = testObject.getExtensionWorkspaceTrustRequestType(extensionMaifest);
assert.strictEqual(workspaceTrustRequest, expected);
}
test('test extension workspace trust request when main entry point is missing', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
instantiationService.stub(IWorkspaceTrustManagementService, getWorkspaceTrustManagementService(true));
const extensionMaifest = getExtensionManifest();
assertWorkspaceTrustRequest(extensionMaifest, 'never');
});
test('test extension workspace trust request when workspace trust is disabled', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
instantiationService.stub(IWorkspaceTrustManagementService, getWorkspaceTrustManagementService(false));
const extensionMaifest = getExtensionManifest({ main: './out/extension.js' });
assertWorkspaceTrustRequest(extensionMaifest, 'never');
});
test('test extension workspace trust request when override exists in settings.json', async () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
instantiationService.stub(IWorkspaceTrustManagementService, getWorkspaceTrustManagementService(true));
await testConfigurationService.setUserConfiguration('security', { workspace: { trust: { extensionRequest: { 'pub.a': { request: 'never' } } } } });
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', workspaceTrust: { request: 'onDemand' } });
assertWorkspaceTrustRequest(extensionMaifest, 'never');
});
test('test extension workspace trust request when override for the version exists in settings.json', async () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
instantiationService.stub(IWorkspaceTrustManagementService, getWorkspaceTrustManagementService(true));
await testConfigurationService.setUserConfiguration('security', { workspace: { trust: { extensionRequest: { 'pub.a': { request: 'never', version: '1.0.0' } } } } });
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', workspaceTrust: { request: 'onDemand' } });
assertWorkspaceTrustRequest(extensionMaifest, 'never');
});
test('test extension workspace trust request when override for a different version exists in settings.json', async () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
instantiationService.stub(IWorkspaceTrustManagementService, getWorkspaceTrustManagementService(true));
await testConfigurationService.setUserConfiguration('security', { workspace: { trust: { extensionRequest: { 'pub.a': { request: 'never', version: '2.0.0' } } } } });
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', workspaceTrust: { request: 'onDemand' } });
assertWorkspaceTrustRequest(extensionMaifest, 'onDemand');
});
test('test extension workspace trust request when default exists in product.json', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{ extensionWorkspaceTrustRequest: { 'pub.a': { default: 'never' } } });
instantiationService.stub(IWorkspaceTrustManagementService, getWorkspaceTrustManagementService(true));
const extensionMaifest = getExtensionManifest({ main: './out/extension.js' });
assertWorkspaceTrustRequest(extensionMaifest, 'never');
});
test('test extension workspace trust request when override exists in product.json', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{ extensionWorkspaceTrustRequest: { 'pub.a': { override: 'onDemand' } } });
instantiationService.stub(IWorkspaceTrustManagementService, getWorkspaceTrustManagementService(true));
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', workspaceTrust: { request: 'never' } });
assertWorkspaceTrustRequest(extensionMaifest, 'onDemand');
});
test('test extension workspace trust request when value exists in package.json', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
instantiationService.stub(IWorkspaceTrustManagementService, getWorkspaceTrustManagementService(true));
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', workspaceTrust: { request: 'onDemand' } });
assertWorkspaceTrustRequest(extensionMaifest, 'onDemand');
});
test('test extension workspace trust request when no value exists in package.json', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
instantiationService.stub(IWorkspaceTrustManagementService, getWorkspaceTrustManagementService(true));
const extensionMaifest = getExtensionManifest({ main: './out/extension.js' });
assertWorkspaceTrustRequest(extensionMaifest, 'onStart');
});
});
function getExtensionManifest(properties: any = {}): IExtensionManifest {
return Object.create({
name: 'a',
publisher: 'pub',
version: '1.0.0',
...properties
}) as IExtensionManifest;
}
function getWorkspaceTrustManagementService(isEnabled: boolean): WorkspaceTrustManagementService {
return new class extends mock<WorkspaceTrustManagementService>() { isWorkspaceTrustEnabled() { return isEnabled; } };
}

View file

@ -17,6 +17,7 @@ import { WorkspaceTrustRequestOptions, IWorkspaceTrustManagementService, IWorksp
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
export const WORKSPACE_TRUST_ENABLED = 'security.workspace.trust.enabled';
export const WORKSPACE_TRUST_EXTENSION_REQUEST = 'security.workspace.trust.extensionRequest';
export const WORKSPACE_TRUST_STORAGE_KEY = 'content.trust.model.key';
export const WorkspaceTrustContext = {