Path: blob/main/src/vs/platform/extensionManagement/common/allowedExtensionsService.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Disposable } from '../../../base/common/lifecycle.js';6import { URI } from '../../../base/common/uri.js';7import * as nls from '../../../nls.js';8import { IGalleryExtension, AllowedExtensionsConfigKey, IAllowedExtensionsService, AllowedExtensionsConfigValueType } from './extensionManagement.js';9import { ExtensionType, IExtension, TargetPlatform } from '../../extensions/common/extensions.js';10import { IProductService } from '../../product/common/productService.js';11import { IMarkdownString, MarkdownString } from '../../../base/common/htmlContent.js';12import { IConfigurationService } from '../../configuration/common/configuration.js';13import { isBoolean, isObject, isUndefined } from '../../../base/common/types.js';14import { Emitter } from '../../../base/common/event.js';1516function isGalleryExtension(extension: any): extension is IGalleryExtension {17return extension.type === 'gallery';18}1920function isIExtension(extension: any): extension is IExtension {21return extension.type === ExtensionType.User || extension.type === ExtensionType.System;22}232425const VersionRegex = /^(?<version>\d+\.\d+\.\d+(-.*)?)(@(?<platform>.+))?$/;2627export class AllowedExtensionsService extends Disposable implements IAllowedExtensionsService {2829_serviceBrand: undefined;3031private readonly publisherOrgs: string[];3233private _allowedExtensionsConfigValue: AllowedExtensionsConfigValueType | undefined;34get allowedExtensionsConfigValue(): AllowedExtensionsConfigValueType | undefined {35return this._allowedExtensionsConfigValue;36}37private _onDidChangeAllowedExtensions = this._register(new Emitter<void>());38readonly onDidChangeAllowedExtensionsConfigValue = this._onDidChangeAllowedExtensions.event;3940constructor(41@IProductService productService: IProductService,42@IConfigurationService protected readonly configurationService: IConfigurationService43) {44super();45this.publisherOrgs = productService.extensionPublisherOrgs?.map(p => p.toLowerCase()) ?? [];46this._allowedExtensionsConfigValue = this.getAllowedExtensionsValue();47this._register(this.configurationService.onDidChangeConfiguration(e => {48if (e.affectsConfiguration(AllowedExtensionsConfigKey)) {49this._allowedExtensionsConfigValue = this.getAllowedExtensionsValue();50this._onDidChangeAllowedExtensions.fire();51}52}));53}5455private getAllowedExtensionsValue(): AllowedExtensionsConfigValueType | undefined {56const value = this.configurationService.getValue<AllowedExtensionsConfigValueType | undefined>(AllowedExtensionsConfigKey);57if (!isObject(value) || Array.isArray(value)) {58return undefined;59}60const entries = Object.entries(value).map(([key, value]) => [key.toLowerCase(), value]);61if (entries.length === 1 && entries[0][0] === '*' && entries[0][1] === true) {62return undefined;63}64return Object.fromEntries(entries);65}6667isAllowed(extension: IGalleryExtension | IExtension | { id: string; publisherDisplayName: string | undefined; version?: string; prerelease?: boolean; targetPlatform?: TargetPlatform }): true | IMarkdownString {68if (!this._allowedExtensionsConfigValue) {69return true;70}7172let id: string, version: string, targetPlatform: TargetPlatform, prerelease: boolean, publisher: string, publisherDisplayName: string | undefined;7374if (isGalleryExtension(extension)) {75id = extension.identifier.id.toLowerCase();76version = extension.version;77prerelease = extension.properties.isPreReleaseVersion;78publisher = extension.publisher.toLowerCase();79publisherDisplayName = extension.publisherDisplayName.toLowerCase();80targetPlatform = extension.properties.targetPlatform;81} else if (isIExtension(extension)) {82id = extension.identifier.id.toLowerCase();83version = extension.manifest.version;84prerelease = extension.preRelease;85publisher = extension.manifest.publisher.toLowerCase();86publisherDisplayName = extension.publisherDisplayName?.toLowerCase();87targetPlatform = extension.targetPlatform;88} else {89id = extension.id.toLowerCase();90version = extension.version ?? '*';91targetPlatform = extension.targetPlatform ?? TargetPlatform.UNIVERSAL;92prerelease = extension.prerelease ?? false;93publisher = extension.id.substring(0, extension.id.indexOf('.')).toLowerCase();94publisherDisplayName = extension.publisherDisplayName?.toLowerCase();95}9697const settingsCommandLink = URI.parse(`command:workbench.action.openSettings?${encodeURIComponent(JSON.stringify({ query: `@id:${AllowedExtensionsConfigKey}` }))}`).toString();98const extensionValue = this._allowedExtensionsConfigValue[id];99const extensionReason = new MarkdownString(nls.localize('specific extension not allowed', "it is not in the [allowed list]({0})", settingsCommandLink));100if (!isUndefined(extensionValue)) {101if (isBoolean(extensionValue)) {102return extensionValue ? true : extensionReason;103}104if (extensionValue === 'stable' && prerelease) {105return new MarkdownString(nls.localize('extension prerelease not allowed', "the pre-release versions of this extension are not in the [allowed list]({0})", settingsCommandLink));106}107if (version !== '*' && Array.isArray(extensionValue) && !extensionValue.some(v => {108const match = VersionRegex.exec(v);109if (match && match.groups) {110const { platform: p, version: v } = match.groups;111if (v !== version) {112return false;113}114if (targetPlatform !== TargetPlatform.UNIVERSAL && p && targetPlatform !== p) {115return false;116}117return true;118}119return false;120})) {121return new MarkdownString(nls.localize('specific version of extension not allowed', "the version {0} of this extension is not in the [allowed list]({1})", version, settingsCommandLink));122}123return true;124}125126const publisherKey = publisherDisplayName && this.publisherOrgs.includes(publisherDisplayName) ? publisherDisplayName : publisher;127const publisherValue = this._allowedExtensionsConfigValue[publisherKey];128if (!isUndefined(publisherValue)) {129if (isBoolean(publisherValue)) {130return publisherValue ? true : new MarkdownString(nls.localize('publisher not allowed', "the extensions from this publisher are not in the [allowed list]({1})", publisherKey, settingsCommandLink));131}132if (publisherValue === 'stable' && prerelease) {133return new MarkdownString(nls.localize('prerelease versions from this publisher not allowed', "the pre-release versions from this publisher are not in the [allowed list]({1})", publisherKey, settingsCommandLink));134}135return true;136}137138if (this._allowedExtensionsConfigValue['*'] === true) {139return true;140}141142return extensionReason;143}144}145146147