Skip to content

Commit

Permalink
Display error message when running kernels from untrusted locations (#…
Browse files Browse the repository at this point in the history
…11624)

* Display error message when running kernels from untrusted locations

* Update change log and version
  • Loading branch information
DonJayamanne authored Oct 11, 2022
1 parent 91f4038 commit 8370952
Show file tree
Hide file tree
Showing 30 changed files with 311 additions and 141 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Changelog

## 2022.9.120 (11 October 2022)
### Enhancements
1. Display an error message (with instructions to resolve the issue) in the cell output when attempting to run a cell against a kernel from an untrusted location.
([#11622](https://github.com/Microsoft/vscode-jupyter/issues/11622))

### Thanks

Thanks to the following projects which we fully rely on to provide some of
our features:

- [Python Extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python)
- [debugpy](https://pypi.org/project/debugpy/)

Also thanks to the various projects we provide integrations with which help
make this extension useful:

- [Jupyter](https://jupyter.org/):
[Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/?badge=latest),
[JupyterHub](https://jupyterhub.readthedocs.io/en/stable/),
[ipywidgets](https://ipywidgets.readthedocs.io/en/latest/),
[nbconvert](https://nbconvert.readthedocs.io/en/latest/)

## 2022.9.110 (11 October 2022)
### Fixes
1. Fixed vulnerability described in [CVE-2022-41083](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-41083)
Expand Down
4 changes: 2 additions & 2 deletions build/azure-pipeline.stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ extends:
- script: python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python --no-cache-dir --implementation py --no-deps --upgrade -r ./requirements.txt
displayName: Install Python libs

# - script: npm run updateBuildNumber
# displayName: Update build number
- script: npm run updateBuildNumber
displayName: Update build number

- script: npm run prePublishBundleStable
displayName: Build
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "jupyter",
"displayName": "Jupyter",
"version": "2022.9.1100000000",
"version": "2022.9.120",
"description": "Jupyter notebook support, interactive programming and computing that supports Intellisense, debugging and more.",
"publisher": "ms-toolsai",
"author": {
Expand Down
3 changes: 1 addition & 2 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -844,8 +844,7 @@
"message": "Failed to interrupt the Kernel.",
"comment": ["{Locked='Kernel'}"]
},
"DataScience.updateSettingToTrustKernelSpecs": "Update setting to trust kernels",
"DataScience.untrustedKernelSpecsHidden": "Kernels found in an insecure location have not been loaded.",
"DataScience.failedToStartAnUntrustedKernelSpec": "The kernel '{0}' was not started as it is located in an insecure location '{1}'. \nClick [here](https://aka.ms/JupyterTrustedKernelPaths) for further details, optionally update the setting [jupyter.kernels.trusted](command:workbench.action.openSettings?[\"jupyter.kernels.trusted\"]) to trust the kernel.",
"jupyter.configuration.jupyter.kernels.trusted.markdownDescription": "Enter fully qualified paths to Kernel specification files that are to be trusted. E.g. 'C:\\Program Data\\Jupyter\\kernels\\python3\\kernel.json'. \n**Note**: Kernels can execute code with user privileges. Click [here](https://aka.ms/JupyterTrustedKernelPaths) for further details.",
"DataScience.kernelDied": {
"message": "The kernel died. View Jupyter [log](command:jupyter.viewOutput) for further details. \nError: {0}...",
Expand Down
14 changes: 12 additions & 2 deletions src/interactive-window/debugger/jupyter/debuggingManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { buildSourceMap } from '../helper';
import { noop } from '../../../platform/common/utils/misc';
import { IInteractiveWindowDebuggingManager } from '../../types';
import { IControllerLoader, IControllerSelection } from '../../../notebooks/controllers/types';
import { IServiceContainer } from '../../../platform/ioc/types';

/**
* The DebuggingManager maintains the mapping between notebook documents and debug sessions.
Expand All @@ -61,9 +62,18 @@ export class InteractiveWindowDebuggingManager
@inject(IDebugLocationTrackerFactory)
private readonly debugLocationTrackerFactory: IDebugLocationTrackerFactory,
@inject(IConfigurationService) private readonly configService: IConfigurationService,
@inject(IDebugService) private readonly debugService: IDebugService
@inject(IDebugService) private readonly debugService: IDebugService,
@inject(IServiceContainer) serviceContainer: IServiceContainer
) {
super(kernelProvider, controllerLoader, controllerSelection, commandManager, appShell, vscNotebook);
super(
kernelProvider,
controllerLoader,
controllerSelection,
commandManager,
appShell,
vscNotebook,
serviceContainer
);
}

public override async activate(): Promise<void> {
Expand Down
11 changes: 10 additions & 1 deletion src/kernels/errors/kernelErrorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,12 +453,21 @@ function getUserFriendlyErrorMessage(error: Error | string, errorContext?: Kerne
return getCombinedErrorMessage(errorPrefix, errorMessage);
}
}
function doesErrorHaveMarkdownLinks(message: string) {
const markdownLinks = new RegExp(/\[([^\[]+)\]\((.*)\)/);
return (markdownLinks.exec(message)?.length ?? 0) > 0;
}
function getCombinedErrorMessage(prefix?: string, message?: string) {
const errorMessage = [prefix || '', message || '']
.map((line) => line.trim())
.filter((line) => line.length > 0)
.join(' \n');
if (errorMessage.length && errorMessage.indexOf('command:jupyter.viewOutput') === -1) {

if (
!doesErrorHaveMarkdownLinks(errorMessage) &&
errorMessage.length &&
errorMessage.indexOf('command:jupyter.viewOutput') === -1
) {
return `${
errorMessage.endsWith('.') ? errorMessage : errorMessage + '.'
} \n${DataScience.viewJupyterLogForFurtherInfo()}`;
Expand Down
27 changes: 27 additions & 0 deletions src/kernels/errors/kernelSpecNotTrustedError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { Uri } from 'vscode';
import { getDisplayPath } from '../../platform/common/platform/fs-paths';
import { DataScience } from '../../platform/common/utils/localize';
import { getDisplayNameOrNameOfKernelConnection } from '../helpers';
import { LocalKernelConnectionMetadata } from '../types';
import { WrappedKernelError } from './types';

/**
* Thrown when we attempt to start a kernel that is not trusted.
*/
export class KernelSpecNotTrustedError extends WrappedKernelError {
constructor(kernelConnectionMetadata: LocalKernelConnectionMetadata) {
super(
DataScience.failedToStartAnUntrustedKernelSpec().format(
getDisplayNameOrNameOfKernelConnection(kernelConnectionMetadata),
kernelConnectionMetadata.kernelSpec.specFile
? getDisplayPath(Uri.file(kernelConnectionMetadata.kernelSpec.specFile))
: ''
),
undefined,
kernelConnectionMetadata
);
}
}
58 changes: 0 additions & 58 deletions src/kernels/hiddenKernelNotification.node.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/kernels/raw/finder/jupyterPaths.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,8 @@ export class JupyterPaths {
paths.add(winPath);
}

if (process.env.ALLUSERSPROFILE) {
paths.add(Uri.file(path.join(process.env.ALLUSERSPROFILE, 'jupyter', 'kernels')));
if (process.env.PROGRAMDATA) {
paths.add(Uri.file(path.join(process.env.PROGRAMDATA, 'jupyter', 'kernels')));
}
} else {
// Unix based
Expand Down
10 changes: 1 addition & 9 deletions src/kernels/raw/finder/localKernelFinder.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import { debounceAsync } from '../../../platform/common/utils/decorators';
import { IPythonExtensionChecker } from '../../../platform/api/types';
import { IInterpreterService } from '../../../platform/interpreter/contracts';
import { EnvironmentType } from '../../../platform/pythonEnvironments/info';
import { ITrustedKernelPaths } from './types';

// This class searches for local kernels.
// First it searches on a global persistent state, then on the installed python interpreters,
Expand Down Expand Up @@ -64,8 +63,7 @@ export class LocalKernelFinder implements ILocalKernelFinder, IExtensionSingleAc
@inject(IInterpreterService) private readonly interpreters: IInterpreterService,
@inject(CondaService) private readonly condaService: CondaService,
@inject(IExtensions) private readonly extensions: IExtensions,
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService,
@inject(ITrustedKernelPaths) private readonly trustedKernelSpecPaths: ITrustedKernelPaths
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService
) {
this._initializedPromise = new Promise<void>((resolve) => {
this._initializeResolve = resolve;
Expand Down Expand Up @@ -341,12 +339,6 @@ export class LocalKernelFinder implements ILocalKernelFinder, IExtensionSingleAc
return this.fs.exists(kernel.interpreter.uri);

case 'startUsingLocalKernelSpec':
if (
kernel.kernelSpec.specFile &&
!this.trustedKernelSpecPaths.isTrusted(Uri.file(kernel.kernelSpec.specFile))
) {
return false;
}
// Spec files have to still exist and interpreters have to exist
const promiseSpec = kernel.kernelSpec.specFile
? this.fs.exists(Uri.file(kernel.kernelSpec.specFile))
Expand Down
7 changes: 1 addition & 6 deletions src/kernels/raw/finder/localKernelSpecFinderBase.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
} from '../../../kernels/types';
import { JupyterKernelSpec } from '../../jupyter/jupyterKernelSpec';
import { getComparisonKey } from '../../../platform/vscode-path/resources';
import { ITrustedKernelPaths } from './types';
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
const flatten = require('lodash/flatten') as typeof import('lodash/flatten');

Expand Down Expand Up @@ -64,8 +63,7 @@ export abstract class LocalKernelSpecFinderBase {
protected readonly fs: IFileSystemNode,
protected readonly workspaceService: IWorkspaceService,
protected readonly extensionChecker: IPythonExtensionChecker,
protected readonly globalState: Memento,
private readonly trustedKernelSpecPaths: ITrustedKernelPaths
protected readonly globalState: Memento
) {}

@testOnlyMethod()
Expand Down Expand Up @@ -139,9 +137,6 @@ export abstract class LocalKernelSpecFinderBase {
globalSpecRootPath?: Uri,
cancelToken?: CancellationToken
): Promise<IJupyterKernelSpec | undefined> {
if (!this.trustedKernelSpecPaths.isTrusted(specPath)) {
return;
}
// This is a backup folder for old kernels created by us.
if (specPath.fsPath.includes(oldKernelsSpecFolderName)) {
return;
Expand Down
6 changes: 2 additions & 4 deletions src/kernels/raw/finder/localKnownPathKernelSpecFinder.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { IFileSystemNode } from '../../../platform/common/platform/types.node';
import { IMemento, GLOBAL_MEMENTO } from '../../../platform/common/types';
import { capturePerfTelemetry, Telemetry } from '../../../telemetry';
import { sendKernelSpecTelemetry } from './helper';
import { ITrustedKernelPaths } from './types';

/**
* This class searches for kernels on the file system in well known paths documented by Jupyter.
Expand All @@ -35,10 +34,9 @@ export class LocalKnownPathKernelSpecFinder extends LocalKernelSpecFinderBase {
@inject(IWorkspaceService) workspaceService: IWorkspaceService,
@inject(JupyterPaths) private readonly jupyterPaths: JupyterPaths,
@inject(IPythonExtensionChecker) extensionChecker: IPythonExtensionChecker,
@inject(IMemento) @named(GLOBAL_MEMENTO) memento: Memento,
@inject(ITrustedKernelPaths) trustedKernelSpecPaths: ITrustedKernelPaths
@inject(IMemento) @named(GLOBAL_MEMENTO) memento: Memento
) {
super(fs, workspaceService, extensionChecker, memento, trustedKernelSpecPaths);
super(fs, workspaceService, extensionChecker, memento);
if (this.oldKernelSpecsFolder) {
traceInfo(
`Old kernelSpecs (created by Jupyter Extension) stored in directory ${this.oldKernelSpecsFolder}`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import { areInterpreterPathsSame } from '../../../platform/pythonEnvironments/in
import { capturePerfTelemetry, Telemetry } from '../../../telemetry';
import { PythonEnvironment } from '../../../platform/pythonEnvironments/info';
import { ResourceSet } from '../../../platform/vscode-path/map';
import { ITrustedKernelPaths } from './types';

/**
* Returns all Python kernels and any related kernels registered in the python environment.
Expand All @@ -53,10 +52,9 @@ export class LocalPythonAndRelatedNonPythonKernelSpecFinder extends LocalKernelS
@inject(IPythonExtensionChecker) extensionChecker: IPythonExtensionChecker,
@inject(LocalKnownPathKernelSpecFinder)
private readonly kernelSpecsFromKnownLocations: LocalKnownPathKernelSpecFinder,
@inject(IMemento) @named(GLOBAL_MEMENTO) globalState: Memento,
@inject(ITrustedKernelPaths) trustedKernelSpecPaths: ITrustedKernelPaths
@inject(IMemento) @named(GLOBAL_MEMENTO) globalState: Memento
) {
super(fs, workspaceService, extensionChecker, globalState, trustedKernelSpecPaths);
super(fs, workspaceService, extensionChecker, globalState);
}
public async listKernelSpecs(resource: Resource, ignoreCache?: boolean, cancelToken?: CancellationToken) {
// Get an id for the workspace folder, if we don't have one, use the fsPath of the resource
Expand Down
15 changes: 15 additions & 0 deletions src/kernels/raw/finder/trustedKernelPaths.web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

'use strict';

import { injectable } from 'inversify';
import { Uri } from 'vscode';
import { ITrustedKernelPaths } from './types';

@injectable()
export class TrustedKernelPaths implements ITrustedKernelPaths {
public isTrusted(_kernelPath: Uri): boolean {
return true;
}
}
7 changes: 1 addition & 6 deletions src/kernels/serviceRegistry.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ import { KernelAutoReconnectMonitor } from './kernelAutoReConnectMonitor';
import { PythonKernelInterruptDaemon } from './raw/finder/pythonKernelInterruptDaemon.node';
import { LocalKernelFinder } from './raw/finder/localKernelFinder.node';
import { DebugStartupCodeProvider } from './debuggerStartupCodeProvider';
import { TrustedKernelPaths } from './raw/finder/trustedKernelSpecPaths.node';
import { TrustedKernelPaths } from './raw/finder/trustedKernelPaths.node';
import { ITrustedKernelPaths } from './raw/finder/types';
import { HiddenKernelNotification } from './hiddenKernelNotification.node';

export function registerTypes(serviceManager: IServiceManager, isDevMode: boolean) {
serviceManager.addSingleton<IExtensionSingleActivationService>(IExtensionSingleActivationService, Activation);
Expand All @@ -56,10 +55,6 @@ export function registerTypes(serviceManager: IServiceManager, isDevMode: boolea
IExtensionSyncActivationService,
PortAttributesProviders
);
serviceManager.addSingleton<IExtensionSyncActivationService>(
IExtensionSyncActivationService,
HiddenKernelNotification
);
serviceManager.addSingleton<IRawNotebookSupportedService>(
IRawNotebookSupportedService,
RawNotebookSupportedService
Expand Down
3 changes: 3 additions & 0 deletions src/kernels/serviceRegistry.web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { CellOutputDisplayIdTracker } from './execution/cellDisplayIdTracker';
import { KernelAutoReConnectFailedMonitor } from './kernelAutoReConnectFailedMonitor';
import { KernelAutoReconnectMonitor } from './kernelAutoReConnectMonitor';
import { DebugStartupCodeProvider } from './debuggerStartupCodeProvider';
import { TrustedKernelPaths } from './raw/finder/trustedKernelPaths.web';
import { ITrustedKernelPaths } from './raw/finder/types';

@injectable()
class RawNotebookSupportedService implements IRawNotebookSupportedService {
Expand Down Expand Up @@ -63,6 +65,7 @@ export function registerTypes(serviceManager: IServiceManager, isDevMode: boolea
KernelAutoReconnectMonitor
);
serviceManager.addSingleton<IKernelProvider>(IKernelProvider, KernelProvider);
serviceManager.addSingleton<ITrustedKernelPaths>(ITrustedKernelPaths, TrustedKernelPaths);
serviceManager.addSingleton<IThirdPartyKernelProvider>(IThirdPartyKernelProvider, ThirdPartyKernelProvider);
serviceManager.addSingleton<PreferredRemoteKernelIdProvider>(
PreferredRemoteKernelIdProvider,
Expand Down
Loading

0 comments on commit 8370952

Please sign in to comment.