Skip to content

Commit 7f98e69

Browse files
Kartik Rajwesm
authored andcommitted
Declare limited support for untrusted workspaces by only supporting Pylance (microsoft/vscode-python#17998)
* Untrusted workspaces * Reactivate extension when trust is granted to a workspace * If Jedi is selected as language server, do not activate it * Only send telemetry which is applicable for untrusted workspaces
1 parent 125477e commit 7f98e69

26 files changed

+361
-95
lines changed
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.

extensions/positron-python/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",

extensions/positron-python/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@
171171
"LanguageService.lsFailedToStart": "We encountered an issue starting the language server. Reverting to Jedi language engine. Check the Python output panel for details.",
172172
"LanguageService.lsFailedToDownload": "We encountered an issue downloading the language server. Reverting to Jedi language engine. Check the Python output panel for details.",
173173
"LanguageService.lsFailedToExtract": "We encountered an issue extracting the language server. Reverting to Jedi language engine. Check the Python output panel for details.",
174+
"LanguageService.untrustedWorkspaceMessage": "Only Pylance is supported in untrusted workspaces, setting language server to None.",
174175
"LanguageService.downloadFailedOutputMessage": "Language server download failed",
175176
"LanguageService.extractionFailedOutputMessage": "Language server extraction failed",
176177
"LanguageService.extractionCompletedOutputMessage": "Language server download complete",

extensions/positron-python/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
}

extensions/positron-python/src/client/activation/activationService.ts

Lines changed: 15 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
@@ -235,6 +237,14 @@ export class LanguageServerExtensionActivationService
235237
}
236238
}
237239

240+
if (
241+
!this.workspaceService.isTrusted &&
242+
serverType !== LanguageServerType.Node &&
243+
serverType !== LanguageServerType.None
244+
) {
245+
this.output.appendLine(LanguageService.untrustedWorkspaceMessage());
246+
serverType = LanguageServerType.None;
247+
}
238248
this.sendTelemetryForChosenLanguageServer(serverType).ignoreErrors();
239249
await this.logStartup(serverType);
240250
let server = this.serviceContainer.get<ILanguageServerActivator>(ILanguageServerActivator, serverType);
@@ -305,7 +315,7 @@ export class LanguageServerExtensionActivationService
305315
resource,
306316
workspacePathNameForGlobalWorkspaces,
307317
);
308-
interpreter = interpreter || (await this.interpreterService.getActiveInterpreter(resource));
318+
interpreter = interpreter || (await this.interpreterService?.getActiveInterpreter(resource));
309319
const interperterPortion = interpreter ? `${interpreter.path}-${interpreter.envName}` : '';
310320
return `${resourcePortion}-${interperterPortion}`;
311321
}

extensions/positron-python/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
}

extensions/positron-python/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
}

extensions/positron-python/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),

extensions/positron-python/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);

extensions/positron-python/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,

0 commit comments

Comments
 (0)