Skip to content

Commit

Permalink
DS: Show banner prompting user to trust notebook (microsoft#12555)
Browse files Browse the repository at this point in the history
  • Loading branch information
joyceerhl committed Jun 25, 2020
1 parent 6afc083 commit 791d7be
Show file tree
Hide file tree
Showing 14 changed files with 97 additions and 4 deletions.
3 changes: 3 additions & 0 deletions package.nls.json
Expand Up @@ -32,6 +32,9 @@
"DataScience.openExportFileNo": "No",
"DataScience.failedExportMessage": "Export failed.",
"DataScience.exportToPDFDependencyMessage": "If you have not installed xelatex (TeX) you will need to do so before you can export to PDF, for further instructions please look [here](https://nbconvert.readthedocs.io/en/latest/install.html#installing-tex). \r\nTo avoid installing xelatex (TeX) you might want to try exporting to HTML and using your browsers \"Print to PDF\" feature.",
"DataScience.launchNotebookTrustPrompt" : "A Notebook could execute harmful code when opened. Some cells & outputs have been hidden. Do you trust this notebook?",
"DataScience.launchNotebookTrustPrompt.yes" : "Trust",
"DataScience.launchNotebookTrustPrompt.no" : "Do not trust",
"python.command.python.viewLanguageServerOutput.title": "Show Language Server Output",
"python.command.python.selectAndRunTestMethod.title": "Run Test Method ...",
"python.command.python.selectAndDebugTestMethod.title": "Debug Test Method ...",
Expand Down
6 changes: 6 additions & 0 deletions src/client/common/utils/localize.ts
Expand Up @@ -985,6 +985,12 @@ export namespace DataScience {
'DataScience.usingPreviewNotebookWithOtherNotebookWarning',
'Using the Preview Notebook Editor along with the stable Notebook Editor is not recommended. Doing so could result in data loss or corruption of notebooks.'
);
export const launchNotebookTrustPrompt = localize(
'DataScience.launchNotebookTrustPrompt',
'A Notebook could execute harmful code when opened. Some cells & outputs have been hidden. Do you trust this notebook?'
);
export const trustNotebook = localize('DataScience.launchNotebookTrustPrompt.yes', 'Trust');
export const doNotTrustNotebook = localize('DataScience.launchNotebookTrustPrompt.no', 'Do not trust');
export const previewNotebookOnlySupportedInVSCInsiders = localize(
'DataScience.previewNotebookOnlySupportedInVSCInsiders',
'The Preview Notebook Editor is supported only in the Insiders version of Visual Studio Code.'
Expand Down
Expand Up @@ -98,6 +98,8 @@ export enum InteractiveWindowMessages {
StopDebugging = 'stop_debugging',
GatherCode = 'gather_code',
GatherCodeToScript = 'gather_code_to_script',
LaunchNotebookTrustPrompt = 'launch_notebook_trust_prompt',
TrustNotebookComplete = 'trust_notebook_complete',
LoadAllCells = 'load_all_cells',
LoadAllCellsComplete = 'load_all_cells_complete',
ScrollToCell = 'scroll_to_cell',
Expand Down Expand Up @@ -389,6 +391,10 @@ export interface INotebookModelModifyChange extends INotebookModelChange {
newCells: ICell[];
oldCells: ICell[];
}
export interface INotebookModelTrustChange extends INotebookModelChange {
kind: 'updateTrust';
isNotebookTrusted: boolean;
}
export interface INotebookModelCellExecutionCountChange extends INotebookModelChange {
kind: 'updateCellExecutionCount';
cellId: string;
Expand Down Expand Up @@ -510,7 +516,8 @@ export type NotebookModelChange =
| INotebookModelEditChange
| INotebookModelVersionChange
| INotebookModelChangeTypeChange
| INotebookModelCellExecutionCountChange;
| INotebookModelCellExecutionCountChange
| INotebookModelTrustChange;

export interface IRunByLine {
cell: ICell;
Expand Down Expand Up @@ -619,6 +626,8 @@ export class IInteractiveWindowMapping {
public [InteractiveWindowMessages.StopDebugging]: never | undefined;
public [InteractiveWindowMessages.GatherCode]: ICell;
public [InteractiveWindowMessages.GatherCodeToScript]: ICell;
public [InteractiveWindowMessages.LaunchNotebookTrustPrompt]: never | undefined;
public [InteractiveWindowMessages.TrustNotebookComplete]: never | undefined;
public [InteractiveWindowMessages.LoadAllCells]: ILoadAllCells;
public [InteractiveWindowMessages.LoadAllCellsComplete]: ILoadAllCells;
public [InteractiveWindowMessages.ScrollToCell]: IScrollToCell;
Expand Down
3 changes: 3 additions & 0 deletions src/client/datascience/interactive-common/synchronization.ts
Expand Up @@ -76,6 +76,7 @@ const messageWithMessageTypes: MessageMapping<IInteractiveWindowMapping> & Messa
[CommonActionType.INSERT_BELOW]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare,
[CommonActionType.INSERT_BELOW_AND_FOCUS_NEW_CELL]: MessageType.other,
[CommonActionType.INTERRUPT_KERNEL]: MessageType.other,
[CommonActionType.LAUNCH_NOTEBOOK_TRUST_PROMPT]: MessageType.other,
[CommonActionType.LOADED_ALL_CELLS]: MessageType.other,
[CommonActionType.LINK_CLICK]: MessageType.other,
[CommonActionType.MOVE_CELL_DOWN]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare,
Expand Down Expand Up @@ -141,6 +142,8 @@ const messageWithMessageTypes: MessageMapping<IInteractiveWindowMapping> & Messa
[InteractiveWindowMessages.IPyWidgetUnhandledKernelMessage]: MessageType.other,
[InteractiveWindowMessages.IPyWidgetWidgetVersionNotSupported]: MessageType.other,
[InteractiveWindowMessages.KernelIdle]: MessageType.other,
[InteractiveWindowMessages.LaunchNotebookTrustPrompt]: MessageType.other,
[InteractiveWindowMessages.TrustNotebookComplete]: MessageType.other,
[InteractiveWindowMessages.LoadAllCells]: MessageType.other,
[InteractiveWindowMessages.LoadAllCellsComplete]: MessageType.other,
[InteractiveWindowMessages.LoadOnigasmAssemblyRequest]: MessageType.other,
Expand Down
35 changes: 35 additions & 0 deletions src/client/datascience/interactive-ipynb/nativeEditor.ts
Expand Up @@ -78,6 +78,7 @@ import {
INotebookProvider,
IStatusProvider,
IThemeFinder,
ITrustService,
WebViewViewChangeEventArgs
} from '../types';
import { NativeEditorSynchronizer } from './nativeEditorSynchronizer';
Expand Down Expand Up @@ -182,6 +183,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
@inject(KernelSwitcher) switcher: KernelSwitcher,
@inject(INotebookProvider) notebookProvider: INotebookProvider,
@inject(UseCustomEditorApi) useCustomEditorApi: boolean,
@inject(ITrustService) private trustService: ITrustService,
@inject(IExperimentService) expService: IExperimentService
) {
super(
Expand Down Expand Up @@ -296,6 +298,10 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
this.handleMessage(message, payload, this.loadCellsComplete);
break;

case InteractiveWindowMessages.LaunchNotebookTrustPrompt:
this.handleMessage(message, payload, this.launchNotebookTrustPrompt);
break;

case InteractiveWindowMessages.RestartKernel:
this.interruptExecution();
break;
Expand Down Expand Up @@ -604,6 +610,35 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
this.workspaceStorage.update(VariableExplorerStateKeys.height, value);
}

private async launchNotebookTrustPrompt() {
const prompts = [localize.DataScience.trustNotebook(), localize.DataScience.doNotTrustNotebook()];
const selection = await this.applicationShell.showInformationMessage(
localize.DataScience.launchNotebookTrustPrompt(),
...prompts
);
if (!selection) {
return;
}
if (this.model && selection === localize.DataScience.trustNotebook() && !this.model.isTrusted) {
try {
const contents = this.model.getContent();
await this.trustService.trustNotebook(this.model.file.toString(), contents);
// Update model trust
this.model.update({
source: 'user',
kind: 'updateTrust',
oldDirty: this.model.isDirty,
newDirty: this.model.isDirty,
isNotebookTrusted: true
});
// Tell UI to update main state
await this.postMessage(InteractiveWindowMessages.TrustNotebookComplete);
} catch (err) {
traceError(err);
}
}
}

private interruptExecution() {
this.executeCancelTokens.forEach((t) => t.cancel());
}
Expand Down
Expand Up @@ -48,7 +48,8 @@ import {
INotebookModel,
INotebookProvider,
IStatusProvider,
IThemeFinder
IThemeFinder,
ITrustService
} from '../types';
import { NativeEditor } from './nativeEditor';
import { NativeEditorSynchronizer } from './nativeEditorSynchronizer';
Expand Down Expand Up @@ -104,6 +105,7 @@ export class NativeEditorOldWebView extends NativeEditor {
@inject(INotebookProvider) notebookProvider: INotebookProvider,
@inject(UseCustomEditorApi) useCustomEditorApi: boolean,
@inject(INotebookStorageProvider) private readonly storage: INotebookStorageProvider,
@inject(ITrustService) trustService: ITrustService,
@inject(IExperimentService) expService: IExperimentService
) {
super(
Expand Down Expand Up @@ -136,6 +138,7 @@ export class NativeEditorOldWebView extends NativeEditor {
switcher,
notebookProvider,
useCustomEditorApi,
trustService,
expService
);
asyncRegistry.push(this);
Expand Down
Expand Up @@ -37,6 +37,13 @@ export namespace CommonEffects {
};
}

export function trustNotebook(arg: CommonReducerArg): IMainState {
return {
...arg.prevState,
isNotebookTrusted: true
};
}

export function startProgress(arg: CommonReducerArg): IMainState {
return {
...arg.prevState,
Expand Down
Expand Up @@ -74,6 +74,11 @@ export namespace Transfer {
return arg.prevState;
}

export function launchNotebookTrustPrompt(arg: CommonReducerArg) {
postActionToExtension(arg, InteractiveWindowMessages.LaunchNotebookTrustPrompt);
return arg.prevState;
}

export function linkClick(arg: CommonReducerArg<CommonActionType, ILinkClickAction>): IMainState {
if (arg.payload.data.href.startsWith('data:image/png')) {
postActionToExtension(arg, InteractiveWindowMessages.SavePng, arg.payload.data.href);
Expand Down
2 changes: 2 additions & 0 deletions src/datascience-ui/interactive-common/redux/reducers/types.ts
Expand Up @@ -58,6 +58,7 @@ export enum CommonActionType {
INSERT_BELOW = 'action.insert_below',
INTERRUPT_KERNEL = 'action.interrupt_kernel_action',
IPYWIDGET_RENDER_FAILURE = 'action.ipywidget_render_failure',
LAUNCH_NOTEBOOK_TRUST_PROMPT = 'action.launch_notebook_trust_prompt',
LOAD_IPYWIDGET_CLASS_SUCCESS = 'action.load_ipywidget_class_success',
LOAD_IPYWIDGET_CLASS_FAILURE = 'action.load_ipywidget_class_failure',
IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED = 'action.ipywidget_widget_version_not_supported',
Expand Down Expand Up @@ -141,6 +142,7 @@ export type CommonActionTypeMapping = {
[CommonActionType.REFRESH_VARIABLES]: never | undefined;
[CommonActionType.OPEN_SETTINGS]: IOpenSettingsAction;
[CommonActionType.FOCUS_INPUT]: never | undefined;
[CommonActionType.LAUNCH_NOTEBOOK_TRUST_PROMPT]: never | undefined;
[CommonActionType.LOAD_IPYWIDGET_CLASS_SUCCESS]: LoadIPyWidgetClassLoadAction;
[CommonActionType.LOAD_IPYWIDGET_CLASS_FAILURE]: ILoadIPyWidgetClassFailureAction;
[CommonActionType.IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED]: NotifyIPyWidgeWidgetVersionNotSupportedAction;
Expand Down
8 changes: 7 additions & 1 deletion src/datascience-ui/interactive-common/trustMessage.tsx
Expand Up @@ -5,6 +5,7 @@ import { IFont } from './mainState';
interface ITrustMessageProps {
isNotebookTrusted: boolean;
font: IFont;
launchNotebookTrustPrompt(): void;
}

export class TrustMessage extends React.PureComponent<ITrustMessageProps> {
Expand All @@ -25,7 +26,12 @@ export class TrustMessage extends React.PureComponent<ITrustMessageProps> {

return (
<div className="kernel-status" style={dynamicFont}>
<div className="kernel-status-section kernel-status-server" style={trustTextWidth} role="button">
<div
className="kernel-status-section kernel-status-section-hoverable kernel-status-status"
style={trustTextWidth}
onClick={this.props.launchNotebookTrustPrompt}
role="button"
>
<div className="kernel-status-text">{text}</div>
</div>
<div className="kernel-status-divider" />
Expand Down
1 change: 1 addition & 0 deletions src/datascience-ui/native-editor/redux/actions.ts
Expand Up @@ -122,6 +122,7 @@ export const actionCreators = {
editorUnmounted: (): CommonAction => createIncomingAction(CommonActionType.UNMOUNT),
selectKernel: (): CommonAction => createIncomingAction(InteractiveWindowMessages.SelectKernel),
selectServer: (): CommonAction => createIncomingAction(CommonActionType.SELECT_SERVER),
launchNotebookTrustPrompt: (): CommonAction => createIncomingAction(CommonActionType.LAUNCH_NOTEBOOK_TRUST_PROMPT),
openSettings: (setting?: string): CommonAction<IOpenSettingsAction> =>
createIncomingActionWithPayload(CommonActionType.OPEN_SETTINGS, { setting }),
getVariableData: (
Expand Down
2 changes: 2 additions & 0 deletions src/datascience-ui/native-editor/redux/reducers/index.ts
Expand Up @@ -59,6 +59,7 @@ export const reducerMap: Partial<INativeEditorActionMapping> = {
[CommonActionType.GATHER_CELL_TO_SCRIPT]: Transfer.gatherToScript,
[CommonActionType.EDITOR_LOADED]: Transfer.started,
[CommonActionType.LOADED_ALL_CELLS]: Transfer.loadedAllCells,
[CommonActionType.LAUNCH_NOTEBOOK_TRUST_PROMPT]: Transfer.launchNotebookTrustPrompt,
[CommonActionType.UNMOUNT]: Creation.unmount,
[CommonActionType.LOAD_IPYWIDGET_CLASS_SUCCESS]: CommonEffects.handleLoadIPyWidgetClassSuccess,
[CommonActionType.LOAD_IPYWIDGET_CLASS_FAILURE]: CommonEffects.handleLoadIPyWidgetClassFailure,
Expand All @@ -74,6 +75,7 @@ export const reducerMap: Partial<INativeEditorActionMapping> = {
[InteractiveWindowMessages.NotebookDirty]: CommonEffects.notebookDirty,
[InteractiveWindowMessages.NotebookClean]: CommonEffects.notebookClean,
[InteractiveWindowMessages.LoadAllCells]: Creation.loadAllCells,
[InteractiveWindowMessages.TrustNotebookComplete]: CommonEffects.trustNotebook,
[InteractiveWindowMessages.NotebookRunAllCells]: Execution.executeAllCells,
[InteractiveWindowMessages.NotebookRunSelectedCell]: Execution.executeSelectedCell,
[InteractiveWindowMessages.NotebookAddCellBelow]: Creation.addAndFocusCell,
Expand Down
12 changes: 11 additions & 1 deletion src/datascience-ui/native-editor/toolbar.tsx
Expand Up @@ -46,6 +46,7 @@ export type INativeEditorToolbarProps = INativeEditorDataProps & {
interruptKernel: typeof actionCreators.interruptKernel;
selectKernel: typeof actionCreators.selectKernel;
selectServer: typeof actionCreators.selectServer;
launchNotebookTrustPrompt: typeof actionCreators.launchNotebookTrustPrompt;
isNotebookTrusted: boolean;
};

Expand Down Expand Up @@ -110,6 +111,11 @@ export class Toolbar extends React.PureComponent<INativeEditorToolbarProps> {
this.props.selectServer();
this.props.sendCommand(NativeMouseCommandTelemetry.SelectServer);
};
const launchNotebookTrustPrompt = () => {
if (!this.props.isNotebookTrusted) {
this.props.launchNotebookTrustPrompt();
}
};
const canRunAbove = (selectedInfo.selectedCellIndex ?? -1) > 0;
const canRunBelow =
(selectedInfo.selectedCellIndex ?? -1) < this.props.cellCount - 1 &&
Expand Down Expand Up @@ -251,7 +257,11 @@ export class Toolbar extends React.PureComponent<INativeEditorToolbarProps> {
</ImageButton>
</div>
<div className={'jupyter-info-container'}>
<TrustMessage font={this.props.font} isNotebookTrusted={this.props.isNotebookTrusted} />
<TrustMessage
isNotebookTrusted={this.props.isNotebookTrusted}
font={this.props.font}
launchNotebookTrustPrompt={launchNotebookTrustPrompt}
/>
<KernelSelection
baseTheme={this.props.baseTheme}
font={this.props.font}
Expand Down
Expand Up @@ -60,6 +60,7 @@ suite('DataScience Native Toolbar', () => {
sendCommand: noopAny,
toggleVariableExplorer: sinon.stub(),
setVariableExplorerHeight: sinon.stub(),
launchNotebookTrustPrompt: sinon.stub(),
variablesVisible: false,
isNotebookTrusted: true
};
Expand Down

0 comments on commit 791d7be

Please sign in to comment.