Path: blob/main/src/vs/platform/extensionManagement/common/allowedExtensionsService.ts
5240 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 * as nls from '../../../nls.js';7import { IGalleryExtension, AllowedExtensionsConfigKey, IAllowedExtensionsService, AllowedExtensionsConfigValueType } from './extensionManagement.js';8import { ExtensionType, IExtension, TargetPlatform } from '../../extensions/common/extensions.js';9import { IProductService } from '../../product/common/productService.js';10import { createCommandUri, IMarkdownString, MarkdownString } from '../../../base/common/htmlContent.js';11import { IConfigurationService } from '../../configuration/common/configuration.js';12import { isBoolean, isObject, isUndefined } from '../../../base/common/types.js';13import { Emitter } from '../../../base/common/event.js';1415function isGalleryExtension(extension: unknown): extension is IGalleryExtension {16return (extension as IGalleryExtension).type === 'gallery';17}1819function isIExtension(extension: unknown): extension is IExtension {20return (extension as IExtension).type === ExtensionType.User || (extension as IExtension).type === ExtensionType.System;21}222324const VersionRegex = /^(?<version>\d+\.\d+\.\d+(-.*)?)(@(?<platform>.+))?$/;2526export class AllowedExtensionsService extends Disposable implements IAllowedExtensionsService {2728_serviceBrand: undefined;2930private readonly publisherOrgs: string[];3132private _allowedExtensionsConfigValue: AllowedExtensionsConfigValueType | undefined;33get allowedExtensionsConfigValue(): AllowedExtensionsConfigValueType | undefined {34return this._allowedExtensionsConfigValue;35}36private _onDidChangeAllowedExtensions = this._register(new Emitter<void>());37readonly onDidChangeAllowedExtensionsConfigValue = this._onDidChangeAllowedExtensions.event;3839constructor(40@IProductService productService: IProductService,41@IConfigurationService protected readonly configurationService: IConfigurationService42) {43super();44this.publisherOrgs = productService.extensionPublisherOrgs?.map(p => p.toLowerCase()) ?? [];45this._allowedExtensionsConfigValue = this.getAllowedExtensionsValue();46this._register(this.configurationService.onDidChangeConfiguration(e => {47if (e.affectsConfiguration(AllowedExtensionsConfigKey)) {48this._allowedExtensionsConfigValue = this.getAllowedExtensionsValue();49this._onDidChangeAllowedExtensions.fire();50}51}));52}5354private getAllowedExtensionsValue(): AllowedExtensionsConfigValueType | undefined {55const value = this.configurationService.getValue<AllowedExtensionsConfigValueType | undefined>(AllowedExtensionsConfigKey);56if (!isObject(value) || Array.isArray(value)) {57return undefined;58}59const entries = Object.entries(value).map(([key, value]) => [key.toLowerCase(), value]);60if (entries.length === 1 && entries[0][0] === '*' && entries[0][1] === true) {61return undefined;62}63return Object.fromEntries(entries);64}6566isAllowed(extension: IGalleryExtension | IExtension | { id: string; publisherDisplayName: string | undefined; version?: string; prerelease?: boolean; targetPlatform?: TargetPlatform }): true | IMarkdownString {67if (!this._allowedExtensionsConfigValue) {68return true;69}7071let id: string, version: string, targetPlatform: TargetPlatform, prerelease: boolean, publisher: string, publisherDisplayName: string | undefined;7273if (isGalleryExtension(extension)) {74id = extension.identifier.id.toLowerCase();75version = extension.version;76prerelease = extension.properties.isPreReleaseVersion;77publisher = extension.publisher.toLowerCase();78publisherDisplayName = extension.publisherDisplayName.toLowerCase();79targetPlatform = extension.properties.targetPlatform;80} else if (isIExtension(extension)) {81id = extension.identifier.id.toLowerCase();82version = extension.manifest.version;83prerelease = extension.preRelease;84publisher = extension.manifest.publisher.toLowerCase();85publisherDisplayName = extension.publisherDisplayName?.toLowerCase();86targetPlatform = extension.targetPlatform;87} else {88id = extension.id.toLowerCase();89version = extension.version ?? '*';90targetPlatform = extension.targetPlatform ?? TargetPlatform.UNIVERSAL;91prerelease = extension.prerelease ?? false;92publisher = extension.id.substring(0, extension.id.indexOf('.')).toLowerCase();93publisherDisplayName = extension.publisherDisplayName?.toLowerCase();94}9596const settingsCommandLink = createCommandUri('workbench.action.openSettings', { query: `@id:${AllowedExtensionsConfigKey}` }).toString();97const extensionValue = this._allowedExtensionsConfigValue[id];98const extensionReason = new MarkdownString(nls.localize('specific extension not allowed', "it is not in the [allowed list]({0})", settingsCommandLink));99if (!isUndefined(extensionValue)) {100if (isBoolean(extensionValue)) {101return extensionValue ? true : extensionReason;102}103if (extensionValue === 'stable' && prerelease) {104return new MarkdownString(nls.localize('extension prerelease not allowed', "the pre-release versions of this extension are not in the [allowed list]({0})", settingsCommandLink));105}106if (version !== '*' && Array.isArray(extensionValue) && !extensionValue.some(v => {107const match = VersionRegex.exec(v);108if (match && match.groups) {109const { platform: p, version: v } = match.groups;110if (v !== version) {111return false;112}113if (targetPlatform !== TargetPlatform.UNIVERSAL && p && targetPlatform !== p) {114return false;115}116return true;117}118return false;119})) {120return 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));121}122return true;123}124125const publisherKey = publisherDisplayName && this.publisherOrgs.includes(publisherDisplayName) ? publisherDisplayName : publisher;126const publisherValue = this._allowedExtensionsConfigValue[publisherKey];127if (!isUndefined(publisherValue)) {128if (isBoolean(publisherValue)) {129return publisherValue ? true : new MarkdownString(nls.localize('publisher not allowed', "the extensions from this publisher are not in the [allowed list]({1})", publisherKey, settingsCommandLink));130}131if (publisherValue === 'stable' && prerelease) {132return 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));133}134return true;135}136137if (this._allowedExtensionsConfigValue['*'] === true) {138return true;139}140141return extensionReason;142}143}144145146