Tighten notebook code cell output sandbox and enable it by default (#664)

* Tighten notebook code cell output sandbox

* Enable sandboxnotebookoutputs by default

* Address feedback
This commit is contained in:
Tanuj Mittal 2021-04-14 11:06:44 -07:00 committed by GitHub
parent 1685b34e2a
commit 68789c5069
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 202 additions and 36 deletions

177
package-lock.json generated
View File

@ -2853,6 +2853,27 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz",
"integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g=="
},
"htmlparser2": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz",
"integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==",
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^3.0.0",
"domutils": "^2.0.0",
"entities": "^2.0.0"
}
},
"postcss": {
"version": "7.0.35",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
"integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
"requires": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
"supports-color": "^6.1.0"
}
},
"react": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz",
@ -2871,6 +2892,30 @@
"object-assign": "^4.1.1",
"scheduler": "^0.20.1"
}
},
"sanitize-html": {
"version": "1.27.5",
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.5.tgz",
"integrity": "sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==",
"requires": {
"htmlparser2": "^4.1.0",
"lodash": "^4.17.15",
"parse-srcset": "^1.0.2",
"postcss": "^7.0.27"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
@ -5440,6 +5485,29 @@
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
},
"@types/sanitize-html": {
"version": "1.27.2",
"resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-1.27.2.tgz",
"integrity": "sha512-DrH26m7CV6PB4YVckjbSIx+xloB7HBolr9Ctm0gZBffSu5dDV4yJKFQGPquJlReVW+xmg59gx+b/8/qYHxZEuw==",
"dev": true,
"requires": {
"htmlparser2": "^4.1.0"
},
"dependencies": {
"htmlparser2": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz",
"integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==",
"dev": true,
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^3.0.0",
"domutils": "^2.0.0",
"entities": "^2.0.0"
}
}
}
},
"@types/scheduler": {
"version": "0.16.1",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz",
@ -9277,8 +9345,7 @@
"deepmerge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
"dev": true
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
},
"default-compare": {
"version": "1.0.0",
@ -12314,14 +12381,48 @@
}
},
"htmlparser2": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz",
"integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==",
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
"integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^3.0.0",
"domutils": "^2.0.0",
"domhandler": "^4.0.0",
"domutils": "^2.5.2",
"entities": "^2.0.0"
},
"dependencies": {
"domhandler": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.1.0.tgz",
"integrity": "sha512-/6/kmsGlMY4Tup/nGVutdrK9yQi4YjWVcVeoQmixpzjOUK1U7pQkvAPHBJeUxOgxF0J8f8lwCJSlCfD0V4CMGQ==",
"requires": {
"domelementtype": "^2.2.0"
},
"dependencies": {
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
}
}
},
"domutils": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.5.2.tgz",
"integrity": "sha512-MHTthCb1zj8f1GVfRpeZUbohQf/HdBos0oX5gZcQFepOZPLLRyj6Wn7XS7EMnY7CVpwv8863u2vyE83Hfu28HQ==",
"requires": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.1.0"
},
"dependencies": {
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
}
}
}
}
},
"http-deceiver": {
@ -15304,6 +15405,11 @@
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="
},
"klona": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz",
"integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA=="
},
"knockout": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/knockout/-/knockout-3.5.1.tgz",
@ -16246,6 +16352,11 @@
"integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
"optional": true
},
"nanoid": {
"version": "3.1.22",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz",
"integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ=="
},
"nanomatch": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@ -17414,27 +17525,24 @@
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
},
"postcss": {
"version": "7.0.35",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
"integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
"version": "8.2.10",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.10.tgz",
"integrity": "sha512-b/h7CPV7QEdrqIxtAf2j31U5ef05uBDuvoXv6L51Q4rcS1jdlXAVKJv+atCFdUXYl9dyTHGyoMzIepwowRJjFw==",
"requires": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
"supports-color": "^6.1.0"
"colorette": "^1.2.2",
"nanoid": "^3.1.22",
"source-map": "^0.6.1"
},
"dependencies": {
"colorette": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
"integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w=="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
@ -18984,14 +19092,29 @@
}
},
"sanitize-html": {
"version": "1.27.5",
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.5.tgz",
"integrity": "sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.3.3.tgz",
"integrity": "sha512-DCFXPt7Di0c6JUnlT90eIgrjs6TsJl/8HYU3KLdmrVclFN4O0heTcVbJiMa23OKVr6aR051XYtsgd8EWwEBwUA==",
"requires": {
"htmlparser2": "^4.1.0",
"lodash": "^4.17.15",
"deepmerge": "^4.2.2",
"escape-string-regexp": "^4.0.0",
"htmlparser2": "^6.0.0",
"is-plain-object": "^5.0.0",
"klona": "^2.0.3",
"parse-srcset": "^1.0.2",
"postcss": "^7.0.27"
"postcss": "^8.0.2"
},
"dependencies": {
"escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
},
"is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
}
}
},
"sax": {

View File

@ -92,6 +92,7 @@
"reflect-metadata": "0.1.13",
"rx-jupyter": "5.5.12",
"rxjs": "6.6.3",
"sanitize-html": "2.3.3",
"styled-components": "4.3.2",
"swr": "0.4.0",
"terser-webpack-plugin": "3.1.0",
@ -124,6 +125,7 @@
"@types/react-dom": "17.0.3",
"@types/react-notification-system": "0.2.39",
"@types/react-redux": "7.1.7",
"@types/sanitize-html": "1.27.2",
"@types/sinon": "2.3.3",
"@types/styled-components": "5.1.1",
"@types/underscore": "1.7.36",

View File

@ -34,7 +34,8 @@ import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext";
import configureStore from "./NotebookComponent/store";
import { CdbAppState, makeCdbRecord } from "./NotebookComponent/types";
import JavaScript from "./NotebookRenderer/outputs/javascript";
import SandboxJavaScript from "./NotebookRenderer/outputs/SandboxJavaScript";
import SanitizedHTML from "./NotebookRenderer/outputs/SanitizedHTML";
export type KernelSpecsDisplay = { name: string; displayName: string };
@ -167,8 +168,10 @@ export class NotebookClientV2 {
"application/vnd.vega.v5+json": NullTransform,
"application/vdom.v1+json": TransformVDOM,
"application/json": Media.Json,
"application/javascript": userContext.features.sandboxNotebookOutputs ? JavaScript : Media.JavaScript,
"text/html": Media.HTML,
"application/javascript": userContext.features.sandboxNotebookOutputs
? SandboxJavaScript
: Media.JavaScript,
"text/html": userContext.features.sandboxNotebookOutputs ? SanitizedHTML : Media.HTML,
"text/markdown": Media.Markdown,
"text/latex": Media.LaTeX,
"image/svg+xml": Media.SVG,

View File

@ -27,7 +27,7 @@ export class IFrameOutputs extends React.PureComponent<ComponentProps & StatePro
return (
<SandboxFrame
style={{ border: "none", width: "100%" }}
sandbox="allow-downloads allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-popups-to-escape-sandbox"
sandbox="allow-downloads allow-forms allow-pointer-lock allow-same-origin allow-scripts"
>
<div className={`nteract-cell-outputs ${hidden ? "hidden" : ""} ${expanded ? "expanded" : ""}`}>
{outputs.map((output, index) => (

View File

@ -41,7 +41,7 @@ export class SandboxFrame extends React.PureComponent<SandboxFrameProps, Sandbox
);
}
componentWillUnmount() {
componentWillUnmount(): void {
this.resizeObserver?.disconnect();
}

View File

@ -12,7 +12,7 @@ interface Props {
mediaType: "text/javascript";
}
export class JavaScript extends React.PureComponent<Props> {
export class SandboxJavaScript extends React.PureComponent<Props> {
static defaultProps = {
data: "",
mediaType: "application/javascript",
@ -23,4 +23,4 @@ export class JavaScript extends React.PureComponent<Props> {
}
}
export default JavaScript;
export default SandboxJavaScript;

View File

@ -0,0 +1,38 @@
import { Media } from "@nteract/outputs";
import React from "react";
import sanitizeHtml from "sanitize-html";
interface Props {
/**
* The HTML string that will be rendered.
*/
data: string;
/**
* The media type associated with the HTML
* string. This defaults to text/html.
*/
mediaType: "text/html";
}
export class SanitizedHTML extends React.PureComponent<Props> {
static defaultProps = {
data: "",
mediaType: "text/html",
};
render(): JSX.Element {
return <Media.HTML data={sanitize(this.props.data)} />;
}
}
function sanitize(html: string): string {
return sanitizeHtml(html, {
allowedTags: false, // allow all tags
allowedAttributes: false, // allow all attrs
transformTags: {
iframe: "iframe-disabled", // disable iframes
},
});
}
export default SanitizedHTML;

View File

@ -27,7 +27,7 @@ export type Features = {
export function extractFeatures(given = new URLSearchParams(window.location.search)): Features {
const downcased = new URLSearchParams();
const set = (value: string, key: string) => downcased.set(key.toLowerCase(), value);
const get = (key: string) => downcased.get("feature." + key) ?? undefined;
const get = (key: string, defaultValue?: string) => downcased.get("feature." + key) ?? defaultValue;
try {
new URLSearchParams(window.parent.location.search).forEach(set);
@ -56,7 +56,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
notebookBasePath: get("notebookbasepath"),
notebookServerToken: get("notebookservertoken"),
notebookServerUrl: get("notebookserverurl"),
sandboxNotebookOutputs: "true" === get("sandboxnotebookoutputs"),
sandboxNotebookOutputs: "true" === get("sandboxnotebookoutputs", "true"),
selfServeType: get("selfservetype"),
pr: get("pr"),
showMinRUSurvey: "true" === get("showminrusurvey"),