mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-19 17:01:13 +00:00
Fix Markdown HTML issue (#658)
This change enables notebooks to escape HTML (which is a vector for malicious attacks).
We import `MarkdownCell` from the `@nteract/stateful-components` sources so that we can point it to the version of `@nteract/markdown` which contains [this fix](e19c7cc590).
This is a temporary workaround from upgrading to `@nteract/stateful-components` to `7.0.0` which causes build and runtime issues see #599).
This commit is contained in:
@@ -2,7 +2,7 @@ import { CellId } from "@nteract/commutable";
|
||||
import { CellType } from "@nteract/commutable/src";
|
||||
import { actions, ContentRef } from "@nteract/core";
|
||||
import { KernelOutputError, StreamText } from "@nteract/outputs";
|
||||
import { Cells, CodeCell, MarkdownCell, RawCell } from "@nteract/stateful-components";
|
||||
import { Cells, CodeCell, RawCell } from "@nteract/stateful-components";
|
||||
import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor";
|
||||
import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor";
|
||||
import TransformMedia from "@nteract/stateful-components/lib/outputs/transform-media";
|
||||
@@ -21,6 +21,7 @@ import CellLabeler from "./decorators/CellLabeler";
|
||||
import HoverableCell from "./decorators/HoverableCell";
|
||||
import KeyboardShortcuts from "./decorators/kbd-shortcuts";
|
||||
import "./default.css";
|
||||
import MarkdownCell from "./markdown-cell";
|
||||
import "./NotebookRenderer.less";
|
||||
import IFrameOutputs from "./outputs/IFrameOutputs";
|
||||
import Prompt from "./Prompt";
|
||||
|
||||
153
src/Explorer/Notebook/NotebookRenderer/markdown-cell.tsx
Normal file
153
src/Explorer/Notebook/NotebookRenderer/markdown-cell.tsx
Normal file
@@ -0,0 +1,153 @@
|
||||
// TODO The purpose of importing this source file https://github.com/nteract/nteract/blob/main/packages/stateful-components/src/cells/markdown-cell.tsx
|
||||
// into our source is to be able to overwrite the version of react-markdown which has this fix ("escape html to false")
|
||||
// https://github.com/nteract/markdown/commit/e19c7cc590a4379fc507f67a7b4228363b9d8631 without having to upgrade
|
||||
// @nteract/stateful-component which causes runtime issues.
|
||||
|
||||
import { ImmutableCell } from "@nteract/commutable/src";
|
||||
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
|
||||
import { MarkdownPreviewer } from "@nteract/markdown";
|
||||
import { defineConfigOption } from "@nteract/mythic-configuration";
|
||||
import { Source } from "@nteract/presentational-components";
|
||||
import Editor, { EditorSlots } from "@nteract/stateful-components/lib/inputs/editor";
|
||||
import React from "react";
|
||||
import { ReactMarkdownProps } from "react-markdown";
|
||||
import { connect } from "react-redux";
|
||||
import { Dispatch } from "redux";
|
||||
|
||||
const { selector: markdownConfig } = defineConfigOption({
|
||||
key: "markdownOptions",
|
||||
label: "Markdown Editor Options",
|
||||
defaultValue: {},
|
||||
});
|
||||
|
||||
interface NamedMDCellSlots {
|
||||
editor?: EditorSlots;
|
||||
toolbar?: () => JSX.Element;
|
||||
}
|
||||
|
||||
interface ComponentProps {
|
||||
id: string;
|
||||
contentRef: ContentRef;
|
||||
cell_type?: "markdown";
|
||||
children?: NamedMDCellSlots;
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
isCellFocused: boolean;
|
||||
isEditorFocused: boolean;
|
||||
cell?: ImmutableCell;
|
||||
markdownOptions: ReactMarkdownProps;
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
focusAboveCell: () => void;
|
||||
focusBelowCell: () => void;
|
||||
focusEditor: () => void;
|
||||
unfocusEditor: () => void;
|
||||
}
|
||||
|
||||
export class PureMarkdownCell extends React.Component<ComponentProps & DispatchProps & StateProps> {
|
||||
render() {
|
||||
const { contentRef, id, cell, children } = this.props;
|
||||
|
||||
const { isEditorFocused, isCellFocused, markdownOptions } = this.props;
|
||||
|
||||
const { focusAboveCell, focusBelowCell, focusEditor, unfocusEditor } = this.props;
|
||||
|
||||
/**
|
||||
* We don't set the editor slots as defaults to support dynamic imports
|
||||
* Users can continue to add the editorSlots as children
|
||||
*/
|
||||
const editor = children?.editor;
|
||||
const toolbar = children?.toolbar;
|
||||
|
||||
const source = cell ? cell.get("source", "") : "";
|
||||
|
||||
return (
|
||||
<div className="nteract-md-cell nteract-cell">
|
||||
<div className="nteract-cell-row">
|
||||
<div className="nteract-cell-gutter">{toolbar && toolbar()}</div>
|
||||
<div className="nteract-cell-body">
|
||||
<MarkdownPreviewer
|
||||
focusAbove={focusAboveCell}
|
||||
focusBelow={focusBelowCell}
|
||||
focusEditor={focusEditor}
|
||||
cellFocused={isCellFocused}
|
||||
editorFocused={isEditorFocused}
|
||||
unfocusEditor={unfocusEditor}
|
||||
source={source}
|
||||
markdownOptions={markdownOptions}
|
||||
>
|
||||
<Source className="nteract-cell-source">
|
||||
<Editor id={id} contentRef={contentRef}>
|
||||
{editor}
|
||||
</Editor>
|
||||
</Source>
|
||||
</MarkdownPreviewer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const makeMapStateToProps = (
|
||||
initialState: AppState,
|
||||
ownProps: ComponentProps
|
||||
): ((state: AppState) => StateProps) => {
|
||||
const { id, contentRef } = ownProps;
|
||||
const mapStateToProps = (state: AppState): StateProps => {
|
||||
const model = selectors.model(state, { contentRef });
|
||||
let isCellFocused = false;
|
||||
let isEditorFocused = false;
|
||||
let cell;
|
||||
|
||||
if (model && model.type === "notebook") {
|
||||
cell = selectors.notebook.cellById(model, { id });
|
||||
isCellFocused = model.cellFocused === id;
|
||||
isEditorFocused = model.editorFocused === id;
|
||||
}
|
||||
|
||||
const markdownOptionsDefaults = {
|
||||
linkTarget: "_blank",
|
||||
};
|
||||
const currentMarkdownOptions = markdownConfig(state);
|
||||
|
||||
const markdownOptions = Object.assign({}, markdownOptionsDefaults, currentMarkdownOptions);
|
||||
|
||||
return {
|
||||
cell,
|
||||
isCellFocused,
|
||||
isEditorFocused,
|
||||
markdownOptions,
|
||||
};
|
||||
};
|
||||
|
||||
return mapStateToProps;
|
||||
};
|
||||
|
||||
const makeMapDispatchToProps = (
|
||||
initialDispatch: Dispatch,
|
||||
ownProps: ComponentProps
|
||||
): ((dispatch: Dispatch) => DispatchProps) => {
|
||||
const { id, contentRef } = ownProps;
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
|
||||
focusAboveCell: () => {
|
||||
dispatch(actions.focusPreviousCell({ id, contentRef }));
|
||||
dispatch(actions.focusPreviousCellEditor({ id, contentRef }));
|
||||
},
|
||||
focusBelowCell: () => {
|
||||
dispatch(actions.focusNextCell({ id, createCellIfUndefined: true, contentRef }));
|
||||
dispatch(actions.focusNextCellEditor({ id, contentRef }));
|
||||
},
|
||||
focusEditor: () => dispatch(actions.focusCellEditor({ id, contentRef })),
|
||||
unfocusEditor: () => dispatch(actions.focusCellEditor({ id: undefined, contentRef })),
|
||||
});
|
||||
|
||||
return mapDispatchToProps;
|
||||
};
|
||||
|
||||
const MarkdownCell = connect(makeMapStateToProps, makeMapDispatchToProps)(PureMarkdownCell);
|
||||
|
||||
export default MarkdownCell;
|
||||
Reference in New Issue
Block a user