Skip to content

Commit 3b9a035

Browse files
author
Kartik Raj
committed
Untrusted workspaces
1 parent d4644dc commit 3b9a035

22 files changed

+325
-79
lines changed

news/1 Enhancements/18031.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Declare limited support for untrusted workspaces by only supporting Pylance.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
},
99
"capabilities": {
1010
"untrustedWorkspaces": {
11-
"supported": false
11+
"supported": "limited",
12+
"description": "Only Partial IntelliSense with Pylance is supported. Cannot execute Python with untrusted files."
1213
},
1314
"virtualWorkspaces": {
1415
"supported": "limited",

src/client/activation/activationManager.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
4040
@inject(IExperimentService) private readonly experiments: IExperimentService,
4141
@inject(IInterpreterPathService) private readonly interpreterPathService: IInterpreterPathService,
4242
) {
43+
if (!this.workspaceService.isTrusted) {
44+
this.activationServices = this.activationServices.filter(
45+
(service) => service.supportedWorkspaceTypes.untrustedWorkspace,
46+
);
47+
this.singleActivationServices = this.singleActivationServices.filter(
48+
(service) => service.supportedWorkspaceTypes.untrustedWorkspace,
49+
);
50+
}
4351
if (this.workspaceService.isVirtualWorkspace) {
4452
this.activationServices = this.activationServices.filter(
4553
(service) => service.supportedWorkspaceTypes.virtualWorkspace,
@@ -80,13 +88,14 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
8088
}
8189
this.activatedWorkspaces.add(key);
8290

83-
if (this.experiments.inExperimentSync(DeprecatePythonPath.experiment)) {
84-
await this.interpreterPathService.copyOldInterpreterStorageValuesToNew(resource);
91+
if (this.workspaceService.isTrusted) {
92+
// Do not interact with interpreters in a untrusted workspace.
93+
if (this.experiments.inExperimentSync(DeprecatePythonPath.experiment)) {
94+
await this.interpreterPathService.copyOldInterpreterStorageValuesToNew(resource);
95+
}
96+
await this.autoSelection.autoSelectInterpreter(resource);
8597
}
86-
8798
await sendActivationTelemetry(this.fileSystem, this.workspaceService, resource);
88-
89-
await this.autoSelection.autoSelectInterpreter(resource);
9099
await Promise.all(this.activationServices.map((item) => item.activate(resource)));
91100
await this.appDiagnostics.performPreStartupHealthCheck(resource);
92101
}

src/client/activation/activationService.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class LanguageServerExtensionActivationService
5757

5858
private readonly output: OutputChannel;
5959

60-
private readonly interpreterService: IInterpreterService;
60+
private readonly interpreterService?: IInterpreterService;
6161

6262
private readonly languageServerChangeHandler: LanguageServerChangeHandler;
6363

@@ -69,14 +69,16 @@ export class LanguageServerExtensionActivationService
6969
) {
7070
this.workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
7171
this.configurationService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
72-
this.interpreterService = this.serviceContainer.get<IInterpreterService>(IInterpreterService);
7372
this.output = this.serviceContainer.get<OutputChannel>(IOutputChannel, STANDARD_OUTPUT_CHANNEL);
7473

7574
const disposables = serviceContainer.get<IDisposableRegistry>(IDisposableRegistry);
7675
disposables.push(this);
7776
disposables.push(this.workspaceService.onDidChangeConfiguration(this.onDidChangeConfiguration.bind(this)));
7877
disposables.push(this.workspaceService.onDidChangeWorkspaceFolders(this.onWorkspaceFoldersChanged, this));
79-
disposables.push(this.interpreterService.onDidChangeInterpreter(this.onDidChangeInterpreter.bind(this)));
78+
if (this.workspaceService.isTrusted) {
79+
this.interpreterService = this.serviceContainer.get<IInterpreterService>(IInterpreterService);
80+
disposables.push(this.interpreterService.onDidChangeInterpreter(this.onDidChangeInterpreter.bind(this)));
81+
}
8082

8183
this.languageServerChangeHandler = new LanguageServerChangeHandler(
8284
this.getCurrentLanguageServerType(),
@@ -93,7 +95,7 @@ export class LanguageServerExtensionActivationService
9395
const stopWatch = new StopWatch();
9496
// Get a new server and dispose of the old one (might be the same one)
9597
this.resource = resource;
96-
const interpreter = await this.interpreterService.getActiveInterpreter(resource);
98+
const interpreter = await this.interpreterService?.getActiveInterpreter(resource);
9799
const key = await this.getKey(resource, interpreter);
98100

99101
// If we have an old server with a different key, then deactivate it as the
@@ -305,7 +307,7 @@ export class LanguageServerExtensionActivationService
305307
resource,
306308
workspacePathNameForGlobalWorkspaces,
307309
);
308-
interpreter = interpreter || (await this.interpreterService.getActiveInterpreter(resource));
310+
interpreter = interpreter || (await this.interpreterService?.getActiveInterpreter(resource));
309311
const interperterPortion = interpreter ? `${interpreter.path}-${interpreter.envName}` : '';
310312
return `${resourcePortion}-${interperterPortion}`;
311313
}

src/client/activation/node/analysisOptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export class NodeLanguageServerAnalysisOptions extends LanguageServerAnalysisOpt
1818
protected async getInitializationOptions() {
1919
return {
2020
experimentationSupport: true,
21+
trustedWorkspaceSupport: true,
2122
};
2223
}
2324
}

src/client/activation/node/languageServerProxy.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { FileBasedCancellationStrategy } from '../common/cancellationUtils';
2323
import { ProgressReporting } from '../progress';
2424
import { ILanguageClientFactory, ILanguageServerFolderService, ILanguageServerProxy } from '../types';
2525
import { traceDecoratorError, traceDecoratorVerbose, traceError } from '../../logging';
26+
import { IWorkspaceService } from '../../common/application/types';
2627

2728
namespace InExperiment {
2829
export const Method = 'python/inExperiment';
@@ -64,6 +65,7 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
6465
@inject(IExperimentService) private readonly experimentService: IExperimentService,
6566
@inject(IInterpreterPathService) private readonly interpreterPathService: IInterpreterPathService,
6667
@inject(IEnvironmentVariablesProvider) private readonly environmentService: IEnvironmentVariablesProvider,
68+
@inject(IWorkspaceService) private readonly workspace: IWorkspaceService,
6769
) {
6870
this.startupCompleted = createDeferred<void>();
6971
}
@@ -127,6 +129,14 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
127129
}
128130
});
129131

132+
this.disposables.push(
133+
this.workspace.onDidGrantWorkspaceTrust(() => {
134+
this.languageClient!.onReady().then(() => {
135+
this.languageClient!.sendNotification('python/workspaceTrusted', { isTrusted: true });
136+
});
137+
}),
138+
);
139+
130140
this.disposables.push(this.languageClient.start());
131141
await this.serverReady();
132142

@@ -224,5 +234,13 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
224234
return { value };
225235
},
226236
);
237+
238+
this.disposables.push(
239+
this.languageClient!.onRequest('python/isTrustedWorkspace', async () => {
240+
return {
241+
isTrusted: this.workspace.isTrusted,
242+
};
243+
}),
244+
);
227245
}
228246
}

src/client/application/diagnostics/applicationDiagnostics.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import { inject, injectable, named } from 'inversify';
77
import { DiagnosticSeverity } from 'vscode';
8+
import { IWorkspaceService } from '../../common/application/types';
89
import { isTestExecution, STANDARD_OUTPUT_CHANNEL } from '../../common/constants';
910
import { IOutputChannel, Resource } from '../../common/types';
1011
import { IServiceContainer } from '../../ioc/types';
@@ -26,7 +27,11 @@ export class ApplicationDiagnostics implements IApplicationDiagnostics {
2627
if (isTestExecution()) {
2728
return;
2829
}
29-
const services = this.serviceContainer.getAll<IDiagnosticsService>(IDiagnosticsService);
30+
let services = this.serviceContainer.getAll<IDiagnosticsService>(IDiagnosticsService);
31+
const workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
32+
if (!workspaceService.isTrusted) {
33+
services = services.filter((item) => item.runInUntrustedWorkspace);
34+
}
3035
// Perform these validation checks in the foreground.
3136
await this.runDiagnostics(
3237
services.filter((item) => !item.runInBackground),

src/client/application/diagnostics/base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export abstract class BaseDiagnosticsService implements IDiagnosticsService, IDi
3535
@unmanaged() protected serviceContainer: IServiceContainer,
3636
@unmanaged() disposableRegistry: IDisposableRegistry,
3737
@unmanaged() public readonly runInBackground: boolean = false,
38+
@unmanaged() public readonly runInUntrustedWorkspace: boolean = false,
3839
) {
3940
this.filterService = serviceContainer.get<IDiagnosticFilterService>(IDiagnosticFilterService);
4041
disposableRegistry.push(this);

src/client/application/diagnostics/checks/envPathVariable.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ export class EnvironmentPathVariableDiagnosticsService extends BaseDiagnosticsSe
4343
@inject(IServiceContainer) serviceContainer: IServiceContainer,
4444
@inject(IDisposableRegistry) disposableRegistry: IDisposableRegistry,
4545
) {
46-
super([DiagnosticCodes.InvalidEnvironmentPathVariableDiagnostic], serviceContainer, disposableRegistry, true);
46+
super(
47+
[DiagnosticCodes.InvalidEnvironmentPathVariableDiagnostic],
48+
serviceContainer,
49+
disposableRegistry,
50+
true,
51+
true,
52+
);
4753
this.platform = this.serviceContainer.get<IPlatformService>(IPlatformService);
4854
this.messageService = serviceContainer.get<IDiagnosticHandlerService<MessageCommandPrompt>>(
4955
IDiagnosticHandlerService,

src/client/application/diagnostics/checks/pylanceDefault.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class PylanceDefaultDiagnosticService extends BaseDiagnosticsService {
4040
protected readonly messageService: IDiagnosticHandlerService<MessageCommandPrompt>,
4141
@inject(IDisposableRegistry) disposableRegistry: IDisposableRegistry,
4242
) {
43-
super([DiagnosticCodes.PylanceDefaultDiagnostic], serviceContainer, disposableRegistry, true);
43+
super([DiagnosticCodes.PylanceDefaultDiagnostic], serviceContainer, disposableRegistry, true, true);
4444

4545
this.initialMementoValue = this.context.globalState.get(EXTENSION_VERSION_MEMENTO);
4646
}

0 commit comments

Comments
 (0)