Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display error message when running kernels from untrusted locations #11624

Merged
merged 2 commits into from
Oct 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renabling this,
@IanMatthewHuff @rebornix @amunger /cc

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