Skip to content

Commit 91f4038

Browse files
committed
Merged PR 560: Do no load kernels from insecure directories
Do no load kernels from insecure directories
1 parent 2910d29 commit 91f4038

16 files changed

+281
-16
lines changed

CHANGELOG.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# Changelog
22

3+
## 2022.9.110 (11 October 2022)
4+
### Fixes
5+
1. Fixed vulnerability described in [CVE-2022-41083](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-41083)
6+
7+
### Thanks
8+
9+
Thanks to the following projects which we fully rely on to provide some of
10+
our features:
11+
12+
- [Python Extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python)
13+
- [debugpy](https://pypi.org/project/debugpy/)
14+
15+
Also thanks to the various projects we provide integrations with which help
16+
make this extension useful:
17+
18+
- [Jupyter](https://jupyter.org/):
19+
[Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/?badge=latest),
20+
[JupyterHub](https://jupyterhub.readthedocs.io/en/stable/),
21+
[ipywidgets](https://ipywidgets.readthedocs.io/en/latest/),
22+
[nbconvert](https://nbconvert.readthedocs.io/en/latest/)
23+
24+
325
## 2022.9.100 (4 October 2022)
426

527
### Enhancements
@@ -2159,4 +2181,4 @@ make this extension useful:
21592181
- [Jupyter](https://jupyter.org/):
21602182
[Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/?badge=latest),
21612183
[JupyterHub](https://jupyterhub.readthedocs.io/en/stable/),
2162-
[ipywidgets](https://ipywidgets.readthedocs.io/en/latest/),
2184+
[ipywidgets](https://ipywidgets.readthedocs.io/en/latest/),

build/azure-pipeline.stable.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ extends:
3939
- script: python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python --no-cache-dir --implementation py --no-deps --upgrade -r ./requirements.txt
4040
displayName: Install Python libs
4141

42-
- script: npm run updateBuildNumber
43-
displayName: Update build number
42+
# - script: npm run updateBuildNumber
43+
# displayName: Update build number
4444

4545
- script: npm run prePublishBundleStable
4646
displayName: Build

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "jupyter",
33
"displayName": "Jupyter",
4-
"version": "2022.9.100",
4+
"version": "2022.9.1100000000",
55
"description": "Jupyter notebook support, interactive programming and computing that supports Intellisense, debugging and more.",
66
"publisher": "ms-toolsai",
77
"author": {
@@ -1888,6 +1888,15 @@
18881888
]
18891889
]
18901890
},
1891+
"jupyter.kernels.trusted": {
1892+
"type": "array",
1893+
"items": {
1894+
"type": "string"
1895+
},
1896+
"default": [],
1897+
"markdownDescription": "%jupyter.configuration.jupyter.kernels.trusted.markdownDescription%",
1898+
"scope": "machine"
1899+
},
18911900
"jupyter.interactiveWindowMode": {
18921901
"type": "string",
18931902
"enum": [

package.nls.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@
342342
"message": "Behavior of the Interactive Window. 'perFile' will create a new interactive window for every file that runs a cell. 'single' allows a single window. 'multiple' allows the creation of multiple.",
343343
"comment": ["{Locked='perFile'}", "{Locked=\"'single'\"}", "{Locked=\"'multiple'\"}"]
344344
},
345-
"jupyter.configuration.jupyter.interactiveWindowViewColumn.description":{
345+
"jupyter.configuration.jupyter.interactiveWindowViewColumn.description": {
346346
"message": "Where to open an Interactive Window that is not associated with a python file. 'beside' will open the interactive window to the right of the active editor. 'active' will open the interactive window in place of the active editor. 'secondGroup' will open the interactive window in the second editor group.",
347347
"comment": ["{Locked='beside'}", "{Locked=\"'active'\"}", "{Locked=\"'secondGroup'\"}"]
348348
},
@@ -844,6 +844,9 @@
844844
"message": "Failed to interrupt the Kernel.",
845845
"comment": ["{Locked='Kernel'}"]
846846
},
847+
"DataScience.updateSettingToTrustKernelSpecs": "Update setting to trust kernels",
848+
"DataScience.untrustedKernelSpecsHidden": "Kernels found in an insecure location have not been loaded.",
849+
"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.",
847850
"DataScience.kernelDied": {
848851
"message": "The kernel died. View Jupyter [log](command:jupyter.viewOutput) for further details. \nError: {0}...",
849852
"comment": ["{Locked='command:jupyter.viewOutput'}"]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
import { inject, injectable, named } from 'inversify';
6+
import { commands, Memento } from 'vscode';
7+
import { IExtensionSyncActivationService } from '../platform/activation/types';
8+
import { IApplicationShell } from '../platform/common/application/types';
9+
import { GLOBAL_MEMENTO, IBrowserService, IMemento } from '../platform/common/types';
10+
import { Common, DataScience } from '../platform/common/utils/localize';
11+
import { noop } from '../platform/common/utils/misc';
12+
import { TrustedKernelPaths } from './raw/finder/trustedKernelSpecPaths.node';
13+
14+
const MEMENTO_KEY_NOTIFIED_ABOUT_HIDDEN_KERNEL = 'MEMENTO_KEY_NOTIFIED_ABOUT_HIDDEN_KERNEL_1';
15+
@injectable()
16+
export class HiddenKernelNotification implements IExtensionSyncActivationService {
17+
private notifiedAboutHiddenKernel?: boolean;
18+
constructor(
19+
@inject(IMemento) @named(GLOBAL_MEMENTO) private readonly globalMemento: Memento,
20+
@inject(IApplicationShell) private readonly appShell: IApplicationShell,
21+
@inject(IBrowserService) private readonly browser: IBrowserService
22+
) {}
23+
24+
public activate(): void {
25+
TrustedKernelPaths.IsKernelSpecHidden.promise
26+
.then((hidden) => {
27+
if (
28+
!hidden ||
29+
this.notifiedAboutHiddenKernel ||
30+
this.globalMemento.get<boolean>(MEMENTO_KEY_NOTIFIED_ABOUT_HIDDEN_KERNEL, false)
31+
) {
32+
return;
33+
}
34+
this.notifiedAboutHiddenKernel = true;
35+
this.globalMemento.update(MEMENTO_KEY_NOTIFIED_ABOUT_HIDDEN_KERNEL, true).then(noop, noop);
36+
this.appShell
37+
.showWarningMessage(
38+
DataScience.untrustedKernelSpecsHidden(),
39+
Common.learnMore(),
40+
DataScience.updateSettingToTrustKernelSpecs()
41+
)
42+
.then((selection) => {
43+
switch (selection) {
44+
case Common.learnMore():
45+
this.browser.launch('https://aka.ms/JupyterTrustedKernelPaths');
46+
break;
47+
case DataScience.updateSettingToTrustKernelSpecs():
48+
commands
49+
.executeCommand('workbench.action.openSettings', 'jupyter.kernels.trusted')
50+
.then(noop, noop);
51+
break;
52+
}
53+
})
54+
.then(noop, noop);
55+
})
56+
.catch(noop);
57+
}
58+
}

src/kernels/raw/finder/localKernelFinder.node.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { debounceAsync } from '../../../platform/common/utils/decorators';
2727
import { IPythonExtensionChecker } from '../../../platform/api/types';
2828
import { IInterpreterService } from '../../../platform/interpreter/contracts';
2929
import { EnvironmentType } from '../../../platform/pythonEnvironments/info';
30+
import { ITrustedKernelPaths } from './types';
3031

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

341343
case 'startUsingLocalKernelSpec':
344+
if (
345+
kernel.kernelSpec.specFile &&
346+
!this.trustedKernelSpecPaths.isTrusted(Uri.file(kernel.kernelSpec.specFile))
347+
) {
348+
return false;
349+
}
342350
// Spec files have to still exist and interpreters have to exist
343351
const promiseSpec = kernel.kernelSpec.specFile
344352
? this.fs.exists(Uri.file(kernel.kernelSpec.specFile))

src/kernels/raw/finder/localKernelSpecFinderBase.node.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
} from '../../../kernels/types';
2525
import { JupyterKernelSpec } from '../../jupyter/jupyterKernelSpec';
2626
import { getComparisonKey } from '../../../platform/vscode-path/resources';
27+
import { ITrustedKernelPaths } from './types';
2728
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
2829
const flatten = require('lodash/flatten') as typeof import('lodash/flatten');
2930

@@ -63,7 +64,8 @@ export abstract class LocalKernelSpecFinderBase {
6364
protected readonly fs: IFileSystemNode,
6465
protected readonly workspaceService: IWorkspaceService,
6566
protected readonly extensionChecker: IPythonExtensionChecker,
66-
protected readonly globalState: Memento
67+
protected readonly globalState: Memento,
68+
private readonly trustedKernelSpecPaths: ITrustedKernelPaths
6769
) {}
6870

6971
@testOnlyMethod()
@@ -137,6 +139,9 @@ export abstract class LocalKernelSpecFinderBase {
137139
globalSpecRootPath?: Uri,
138140
cancelToken?: CancellationToken
139141
): Promise<IJupyterKernelSpec | undefined> {
142+
if (!this.trustedKernelSpecPaths.isTrusted(specPath)) {
143+
return;
144+
}
140145
// This is a backup folder for old kernels created by us.
141146
if (specPath.fsPath.includes(oldKernelsSpecFolderName)) {
142147
return;

src/kernels/raw/finder/localKnownPathKernelSpecFinder.node.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { IFileSystemNode } from '../../../platform/common/platform/types.node';
2121
import { IMemento, GLOBAL_MEMENTO } from '../../../platform/common/types';
2222
import { capturePerfTelemetry, Telemetry } from '../../../telemetry';
2323
import { sendKernelSpecTelemetry } from './helper';
24+
import { ITrustedKernelPaths } from './types';
2425

2526
/**
2627
* This class searches for kernels on the file system in well known paths documented by Jupyter.
@@ -34,9 +35,10 @@ export class LocalKnownPathKernelSpecFinder extends LocalKernelSpecFinderBase {
3435
@inject(IWorkspaceService) workspaceService: IWorkspaceService,
3536
@inject(JupyterPaths) private readonly jupyterPaths: JupyterPaths,
3637
@inject(IPythonExtensionChecker) extensionChecker: IPythonExtensionChecker,
37-
@inject(IMemento) @named(GLOBAL_MEMENTO) memento: Memento
38+
@inject(IMemento) @named(GLOBAL_MEMENTO) memento: Memento,
39+
@inject(ITrustedKernelPaths) trustedKernelSpecPaths: ITrustedKernelPaths
3840
) {
39-
super(fs, workspaceService, extensionChecker, memento);
41+
super(fs, workspaceService, extensionChecker, memento, trustedKernelSpecPaths);
4042
if (this.oldKernelSpecsFolder) {
4143
traceInfo(
4244
`Old kernelSpecs (created by Jupyter Extension) stored in directory ${this.oldKernelSpecsFolder}`

src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { areInterpreterPathsSame } from '../../../platform/pythonEnvironments/in
3333
import { capturePerfTelemetry, Telemetry } from '../../../telemetry';
3434
import { PythonEnvironment } from '../../../platform/pythonEnvironments/info';
3535
import { ResourceSet } from '../../../platform/vscode-path/map';
36+
import { ITrustedKernelPaths } from './types';
3637

3738
/**
3839
* Returns all Python kernels and any related kernels registered in the python environment.
@@ -52,9 +53,10 @@ export class LocalPythonAndRelatedNonPythonKernelSpecFinder extends LocalKernelS
5253
@inject(IPythonExtensionChecker) extensionChecker: IPythonExtensionChecker,
5354
@inject(LocalKnownPathKernelSpecFinder)
5455
private readonly kernelSpecsFromKnownLocations: LocalKnownPathKernelSpecFinder,
55-
@inject(IMemento) @named(GLOBAL_MEMENTO) globalState: Memento
56+
@inject(IMemento) @named(GLOBAL_MEMENTO) globalState: Memento,
57+
@inject(ITrustedKernelPaths) trustedKernelSpecPaths: ITrustedKernelPaths
5658
) {
57-
super(fs, workspaceService, extensionChecker, globalState);
59+
super(fs, workspaceService, extensionChecker, globalState, trustedKernelSpecPaths);
5860
}
5961
public async listKernelSpecs(resource: Resource, ignoreCache?: boolean, cancelToken?: CancellationToken) {
6062
// Get an id for the workspace folder, if we don't have one, use the fsPath of the resource

0 commit comments

Comments
 (0)