Skip to content

Commit 88e21ab

Browse files
test
Signed-off-by: Roman Nikitenko <rnikiten@redhat.com>
1 parent 86ea408 commit 88e21ab

5 files changed

Lines changed: 91 additions & 4 deletions

File tree

code/src/vs/platform/extensionManagement/common/extensionManagement.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,7 @@ export async function computeSize(location: URI, fileService: IFileService): Pro
709709
export const ExtensionsLocalizedLabel = localize2('extensions', "Extensions");
710710
export const PreferencesLocalizedLabel = localize2('preferences', 'Preferences');
711711
export const AllowedExtensionsConfigKey = 'extensions.allowed';
712+
export const BlockNonGalleryExtensionsConfigKey = 'extensions.blockNonGalleryExtensions';
712713
export const VerifyExtensionSignatureConfigKey = 'extensions.verifySignature';
713714

714715
Registry.as<IConfigurationRegistry>(Extensions.Configuration)
@@ -781,6 +782,17 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration)
781782
],
782783
}
783784
}
785+
},
786+
[BlockNonGalleryExtensionsConfigKey]: {
787+
type: 'boolean',
788+
markdownDescription: localize('extensions.blockNonGalleryExtensions', "When enabled and the allowed extensions policy is configured, blocks installation of non-gallery extensions (VSIX files and resource extensions). Only extensions from the gallery can be installed."),
789+
default: false,
790+
scope: ConfigurationScope.APPLICATION,
791+
policy: {
792+
name: 'BlockNonGalleryExtensions',
793+
minimumVersion: '1.96',
794+
description: localize('extensions.blockNonGalleryExtensions.policy', "When enabled and the allowed extensions policy is configured, blocks installation of non-gallery extensions (VSIX files and resource extensions). Only extensions from the gallery can be installed."),
795+
},
784796
}
785797
}
786798
});

code/src/vs/server/node/serverEnvironmentService.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import * as fs from 'fs';
67
import * as nls from '../../nls.js';
78

89
import { NativeEnvironmentService } from '../../platform/environment/node/environmentService.js';
@@ -14,6 +15,7 @@ import { URI } from '../../base/common/uri.js';
1415
import { joinPath } from '../../base/common/resources.js';
1516
import { join } from '../../base/common/path.js';
1617

18+
export const POLICY_FILE_PATH = '/checode-config/policy.json';
1719
export const serverOptions: OptionDescriptions<Required<ServerParsedArgs>> = {
1820

1921
/* ----- server setup ----- */
@@ -239,5 +241,12 @@ export class ServerEnvironmentService extends NativeEnvironmentService implement
239241
get machineSettingsResource(): URI { return joinPath(URI.file(join(this.userDataPath, 'Machine')), 'settings.json'); }
240242
@memoize
241243
get mcpResource(): URI { return joinPath(URI.file(join(this.userDataPath, 'User')), 'mcp.json'); }
244+
@memoize
245+
override get policyFile(): URI | undefined {
246+
if (fs.existsSync(POLICY_FILE_PATH)) {
247+
return URI.file(POLICY_FILE_PATH);
248+
}
249+
return super.policyFile;
250+
}
242251
override get args(): ServerParsedArgs { return super.args as ServerParsedArgs; }
243252
}

code/src/vs/server/node/serverServices.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ import { IExtensionsScannerService } from '../../platform/extensionManagement/co
6565
import { ExtensionsScannerService } from './extensionsScannerService.js';
6666
import { IExtensionsProfileScannerService } from '../../platform/extensionManagement/common/extensionsProfileScannerService.js';
6767
import { IUserDataProfilesService } from '../../platform/userDataProfile/common/userDataProfile.js';
68-
import { NullPolicyService } from '../../platform/policy/common/policy.js';
68+
import { IPolicyService, NullPolicyService } from '../../platform/policy/common/policy.js';
69+
import { FilePolicyService } from '../../platform/policy/common/filePolicyService.js';
70+
import { PolicyChannel } from '../../platform/policy/common/policyIpc.js';
6971
import { OneDataSystemAppender } from '../../platform/telemetry/node/1dsAppender.js';
7072
import { LoggerService } from '../../platform/log/node/loggerService.js';
7173
import { ServerUserDataProfilesService } from '../../platform/userDataProfile/node/userDataProfile.js';
@@ -138,8 +140,17 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
138140
const uriIdentityService = new UriIdentityService(fileService);
139141
services.set(IUriIdentityService, uriIdentityService);
140142

143+
let policyService: IPolicyService;
144+
if (environmentService.policyFile) {
145+
policyService = disposables.add(new FilePolicyService(environmentService.policyFile, fileService, logService));
146+
logService.info(`Using policy file: ${environmentService.policyFile.fsPath}`);
147+
} else {
148+
policyService = new NullPolicyService();
149+
logService.info('Policy file not found');
150+
}
151+
141152
// Configuration
142-
const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, new NullPolicyService(), logService);
153+
const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, policyService, logService);
143154
services.set(IConfigurationService, configurationService);
144155

145156
// User Data Profiles
@@ -258,6 +269,9 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
258269

259270
socketServer.registerChannel('mcpManagement', new McpManagementChannel(mcpManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority)));
260271

272+
// Policy Channel - register after policy service is initialized
273+
socketServer.registerChannel('policy', new PolicyChannel(policyService));
274+
261275
// clean up extensions folder
262276
remoteExtensionsScanner.whenExtensionsReady().then(() => extensionManagementService.cleanUp());
263277

code/src/vs/workbench/browser/web.main.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ import { IProgressService } from '../../platform/progress/common/progress.js';
6868
import { DelayedLogChannel } from '../services/output/common/delayedLogChannel.js';
6969
import { dirname, joinPath } from '../../base/common/resources.js';
7070
import { IUserDataProfile, IUserDataProfilesService } from '../../platform/userDataProfile/common/userDataProfile.js';
71-
import { NullPolicyService } from '../../platform/policy/common/policy.js';
71+
import { IPolicyService, NullPolicyService } from '../../platform/policy/common/policy.js';
72+
import { PolicyChannelClient } from '../../platform/policy/common/policyIpc.js';
7273
import { IRemoteExplorerService } from '../services/remote/common/remoteExplorerService.js';
7374
import { DisposableTunnel, TunnelProtocol } from '../../platform/tunnel/common/tunnel.js';
7475
import { ILabelService } from '../../platform/label/common/label.js';
@@ -567,7 +568,32 @@ export class BrowserMain extends Disposable {
567568
}
568569

569570
const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData, Schemas.tmp] /* Cache all non native resources */, environmentService, fileService);
570-
const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, new NullPolicyService());
571+
572+
// Get policy service from remote agent if available, otherwise use NullPolicyService
573+
let policyService: IPolicyService;
574+
if (this.configuration.remoteAuthority) {
575+
try {
576+
const connection = remoteAgentService.getConnection();
577+
if (connection) {
578+
const policyChannel = connection.getChannel('policy');
579+
// PolicyChannelClient needs initial policiesData - start with empty object, policies will be loaded when definitions are registered
580+
policyService = new PolicyChannelClient({}, policyChannel);
581+
logService.info('Policy channel client was created successfully');
582+
} else {
583+
logService.warn('Failed to get remote aget connection, using NullPolicyService');
584+
policyService = new NullPolicyService();
585+
}
586+
} catch (error) {
587+
console.log('/// web main /// connection - ERROR ');
588+
logService.warn('Failed to create policy channel client, using NullPolicyService', error);
589+
policyService = new NullPolicyService();
590+
}
591+
} else {
592+
logService.warn('Failed to create policy channel client(no remote authority), using NullPolicyService');
593+
policyService = new NullPolicyService();
594+
}
595+
596+
const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService);
571597

572598
try {
573599
await workspaceService.initialize(workspace);

code/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
TargetPlatformToString,
2222
IAllowedExtensionsService,
2323
AllowedExtensionsConfigKey,
24+
BlockNonGalleryExtensionsConfigKey,
2425
EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT,
2526
ExtensionManagementError,
2627
ExtensionManagementErrorCode,
@@ -2406,6 +2407,12 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
24062407
}
24072408

24082409
if (extension.gallery) {
2410+
// Check if extension is allowed first (before checking platform compatibility or signing)
2411+
const allowedResult = this.allowedExtensionsService.isAllowed({ id: extension.gallery.identifier.id, publisherDisplayName: extension.gallery.publisherDisplayName });
2412+
if (allowedResult !== true) {
2413+
return new MarkdownString(nls.localize('extension not allowed to install', "This extension cannot be installed because it is not in the allowed list."));
2414+
}
2415+
24092416
if (!extension.gallery.isSigned && shouldRequireRepositorySignatureFor(extension.private, await this.extensionGalleryManifestService.getExtensionGalleryManifest())) {
24102417
return new MarkdownString().appendText(nls.localize('not signed', "This extension is not signed."));
24112418
}
@@ -2428,6 +2435,13 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
24282435
return localResult ?? remoteResult ?? webResult ?? new MarkdownString().appendText(nls.localize('cannot be installed', "Cannot install the '{0}' extension because it is not available in this setup.", extension.displayName ?? extension.identifier.id));
24292436
}
24302437

2438+
// Block non-gallery extensions if the policy is configured and blockNonGalleryExtensions is enabled
2439+
const blockNonGallery = this.configurationService.getValue<boolean>(BlockNonGalleryExtensionsConfigKey);
2440+
const hasPolicy = this.configurationService.inspect(AllowedExtensionsConfigKey).policy !== undefined;
2441+
if (hasPolicy && blockNonGallery) {
2442+
return new MarkdownString(nls.localize('non-gallery extension not allowed', "This extension cannot be installed because only extensions from the gallery are allowed. Please install this extension from the Extensions marketplace."));
2443+
}
2444+
24312445
if (extension.resourceExtension && await this.extensionManagementService.canInstall(extension.resourceExtension) === true) {
24322446
return true;
24332447
}
@@ -2564,9 +2578,21 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
25642578
}
25652579
}
25662580
if (installable instanceof URI) {
2581+
// Block VSIX installations if the policy is configured and blockNonGalleryExtensions is enabled
2582+
const blockNonGallery = this.configurationService.getValue<boolean>(BlockNonGalleryExtensionsConfigKey);
2583+
const hasPolicy = this.configurationService.inspect(AllowedExtensionsConfigKey).policy !== undefined;
2584+
if (hasPolicy && blockNonGallery) {
2585+
throw new Error(nls.localize('vsix not allowed', "VSIX files cannot be installed because only extensions from the gallery are allowed. Please install this extension from the Extensions marketplace."));
2586+
}
25672587
extension = await this.doInstall(undefined, () => this.installFromVSIX(installable, installOptions), progressLocation);
25682588
} else if (extension) {
25692589
if (extension.resourceExtension) {
2590+
// Block resource extension installations if the policy is configured and blockNonGalleryExtensions is enabled
2591+
const blockNonGallery = this.configurationService.getValue<boolean>(BlockNonGalleryExtensionsConfigKey);
2592+
const hasPolicy = this.configurationService.inspect(AllowedExtensionsConfigKey).policy !== undefined;
2593+
if (hasPolicy && blockNonGallery) {
2594+
throw new Error(nls.localize('resource extension not allowed', "Resource extensions cannot be installed because only extensions from the gallery are allowed. Please install this extension from the Extensions marketplace."));
2595+
}
25702596
extension = await this.doInstall(extension, () => this.extensionManagementService.installResourceExtension(installable as IResourceExtension, installOptions), progressLocation);
25712597
} else {
25722598
extension = await this.doInstall(extension, () => this.installFromGallery(extension!, installable as IGalleryExtension, installOptions, servers), progressLocation);

0 commit comments

Comments
 (0)