High severityNVD Advisory· Published Aug 9, 2021· Updated Aug 3, 2024
Failure to Sanitize Special Elements into a Different Plane (Special Element Injection) in notebook
CVE-2021-32798
Description
The Jupyter notebook is a web-based notebook environment for interactive computing. In affected versions untrusted notebook can execute code on load. Jupyter Notebook uses a deprecated version of Google Caja to sanitize user inputs. A public Caja bypass can be used to trigger an XSS when a victim opens a malicious ipynb document in Jupyter Notebook. The XSS allows an attacker to execute arbitrary code on the victim computer using Jupyter APIs.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
notebookPyPI | < 5.7.11 | 5.7.11 |
notebookPyPI | >= 6.0.0, < 6.4.1 | 6.4.1 |
Affected products
1Patches
179fc76e890a8Merge pull request from GHSA-hwvq-6gjx-j797
6 files changed · +39 −118
bower.json+0 −1 modified@@ -9,7 +9,6 @@ "create-react-class": "https://cdn.jsdelivr.net/npm/create-react-class@15.6.3/create-react-class.min.js", "es6-promise": "~1.0", "font-awesome": "components/font-awesome#~4.7.0", - "google-caja": "5669", "jed": "~1.1.1", "jquery": "components/jquery#~3.5.0", "jquery-typeahead": "~2.10.6",
notebook/static/base/js/namespace.js+1 −1 modified@@ -73,7 +73,7 @@ define(function(){ // tree jglobal('SessionList','tree/js/sessionlist'); - Jupyter.version = "6.4.0"; + Jupyter.version = "6.5.0.dev0"; Jupyter._target = '_blank'; return Jupyter;
notebook/static/base/js/security.js+11 −112 modified@@ -3,124 +3,24 @@ define([ 'jquery', - 'components/google-caja/html-css-sanitizer-minified', -], function($, sanitize) { + 'components/sanitizer/index', +], function($, sanitizer) { "use strict"; - + var noop = function (x) { return x; }; - - var caja; - if (window && window.html) { - caja = window.html; - caja.html4 = window.html4; - caja.sanitizeStylesheet = window.sanitizeStylesheet; - } - - var sanitizeAttribs = function (tagName, attribs, opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger) { - /** - * add trusting data-attributes to the default sanitizeAttribs from caja - * this function is mostly copied from the caja source - */ - var ATTRIBS = caja.html4.ATTRIBS; - for (var i = 0; i < attribs.length; i += 2) { - var attribName = attribs[i]; - if (attribName.substr(0,5) == 'data-') { - var attribKey = '*::' + attribName; - if (!ATTRIBS.hasOwnProperty(attribKey)) { - ATTRIBS[attribKey] = 0; - } - } - } - // Caja doesn't allow data uri for img::src, see - // https://github.com/google/caja/issues/1558 - // This is not a security issue for browser post ie6 though, so we - // disable the check - // https://www.owasp.org/index.php/Script_in_IMG_tags - ATTRIBS['img::src'] = 0; - return caja.sanitizeAttribs(tagName, attribs, opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger); - }; - - var sanitize_css = function (css, tagPolicy) { - /** - * sanitize CSS - * like sanitize_html, but for CSS - * called by sanitize_stylesheets - */ - return caja.sanitizeStylesheet( - window.location.pathname, - css, - { - containerClass: null, - idSuffix: '', - tagPolicy: tagPolicy, - virtualizeAttrName: noop - }, - noop - ); - }; - - var sanitize_stylesheets = function (html, tagPolicy) { - /** - * sanitize just the css in style tags in a block of html - * called by sanitize_html, if allow_css is true - */ - var h = $("<div/>").append(html); - var style_tags = h.find("style"); - if (!style_tags.length) { - // no style tags to sanitize - return html; - } - style_tags.each(function(i, style) { - style.innerHTML = sanitize_css(style.innerHTML, tagPolicy); - }); - return h.html(); - }; - + var defaultSanitizer = sanitizer.defaultSanitizer; + var sanitize_html = function (html, allow_css) { /** * sanitize HTML * if allow_css is true (default: false), CSS is sanitized as well. * otherwise, CSS elements and attributes are simply removed. */ - var html4 = caja.html4; - - if (allow_css) { - // allow sanitization of style tags, - // not just scrubbing - html4.ELEMENTS.style &= ~html4.eflags.UNSAFE; - html4.ATTRIBS.style = html4.atype.STYLE; - } else { - // scrub all CSS - html4.ELEMENTS.style |= html4.eflags.UNSAFE; - html4.ATTRIBS.style = html4.atype.SCRIPT; - } - - var record_messages = function (msg, opts) { - console.log("HTML Sanitizer", msg, opts); - }; - - var policy = function (tagName, attribs) { - if (!(html4.ELEMENTS[tagName] & html4.eflags.UNSAFE)) { - return { - 'attribs': sanitizeAttribs(tagName, attribs, - noop, noop, record_messages) - }; - } else { - record_messages(tagName + " removed", { - change: "removed", - tagName: tagName - }); - } - }; - - var sanitized = caja.sanitizeWithPolicy(html, policy); - - if (allow_css) { - // sanitize style tags as stylesheets - sanitized = sanitize_stylesheets(sanitized, policy); - } - - return sanitized; + const options = {}; + if (!allow_css) { + options.allowedStyles = {}; + } + return defaultSanitizer.sanitize(html, options); }; var sanitize_html_and_parse = function (html, allow_css) { @@ -141,9 +41,8 @@ define([ $.htmlPrefilter = prev_htmlPrefilter; // Set it back again } }; - + var security = { - caja: caja, sanitize_html_and_parse: sanitize_html_and_parse, sanitize_html: sanitize_html };
package.json+5 −1 modified@@ -12,14 +12,18 @@ "scripts": { "bower": "bower install", "build": "python setup.py js css", + "build:webpack": "webpack --mode development", "build:watch": "npm run watch", "watch": "onchange 'notebook/static/**/!(*.min).js' 'notebook/static/**/*.less' 'bower.json' -- npm run build" }, "devDependencies": { + "@jupyterlab/apputils": "^3.1.3", "bower": "^1.8.8", "less": "~2", "onchange": "^6.0.0", "po2json": "^0.4.5", - "requirejs": "^2.3.6" + "requirejs": "^2.3.6", + "webpack": "^5.46.0", + "webpack-cli": "^4.7.2" } }
setupbase.py+12 −3 modified@@ -137,7 +137,6 @@ def find_package_data(): pjoin(components, "font-awesome", "css", "*.css"), pjoin(components, "es6-promise", "*.js"), pjoin(components, "font-awesome", "fonts", "*.*"), - pjoin(components, "google-caja", "html-css-sanitizer-minified.js"), pjoin(components, "jed", "jed.js"), pjoin(components, "jquery", "jquery.min.js"), pjoin(components, "jquery-typeahead", "dist", "jquery.typeahead.min.js"), @@ -151,6 +150,7 @@ def find_package_data(): pjoin(components, "requirejs", "require.js"), pjoin(components, "requirejs-plugins", "src", "json.js"), pjoin(components, "requirejs-text", "text.js"), + pjoin(components, "sanitizer", "index.js"), pjoin(components, "underscore", "underscore-min.js"), pjoin(components, "moment", "moment.js"), pjoin(components, "moment", "min", "*.js"), @@ -374,14 +374,21 @@ def finalize_options(self): bower_dir = pjoin(static, 'components') node_modules = pjoin(repo_root, 'node_modules') + sanitizer_dir = pjoin(bower_dir, 'sanitizer') def should_run(self): if self.force: return True if not os.path.exists(self.bower_dir): return True - - return mtime(self.bower_dir) < mtime(pjoin(repo_root, 'bower.json')) + if not os.path.exists(self.sanitizer_dir): + return True + + bower_stale = mtime(self.bower_dir) < mtime(pjoin(repo_root, 'bower.json')) + if bower_stale: + return True + + return mtime(self.sanitizer_dir) < mtime(pjoin(repo_root, 'webpack.config.js')) def should_run_npm(self): if not which('npm'): @@ -415,6 +422,8 @@ def run(self): print("You can install js dependencies with `npm install`", file=sys.stderr) raise # self.npm_components() + if not os.path.exists(self.sanitizer_dir): + run(['npm', 'run', 'build:webpack'], cwd=repo_root, env=env) os.utime(self.bower_dir, None) # update package data in case this created new files update_package_data(self.distribution)
webpack.config.js+10 −0 added@@ -0,0 +1,10 @@ +const path = require('path'); + +module.exports = { + entry: '@jupyterlab/apputils/lib/sanitizer', + output: { + filename: 'index.js', + path: path.resolve(__dirname, 'notebook/static/components/sanitizer'), + libraryTarget: "amd" + } +}
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-hwvq-6gjx-j797ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-32798ghsaADVISORY
- github.com/jupyter/notebook/commit/79fc76e890a8ec42f73a3d009e44ef84c14ef0d5ghsax_refsource_MISCWEB
- github.com/jupyter/notebook/security/advisories/GHSA-hwvq-6gjx-j797ghsax_refsource_CONFIRMWEB
- github.com/pypa/advisory-database/tree/main/vulns/notebook/PYSEC-2021-118.yamlghsaWEB
News mentions
0No linked articles in our index yet.