VYPR
High severityNVD Advisory· Published Aug 28, 2024· Updated Aug 28, 2024

HTML injection in Jupyter Notebook and JupyterLab leading to DOM Clobbering

CVE-2024-43805

Description

jupyterlab is an extensible environment for interactive and reproducible computing, based on the Jupyter Notebook Architecture. This vulnerability depends on user interaction by opening a malicious notebook with Markdown cells, or Markdown file using JupyterLab preview feature. A malicious user can access any data that the attacked user has access to as well as perform arbitrary requests acting as the attacked user. JupyterLab v3.6.8, v4.2.5 and Jupyter Notebook v7.2.2 have been patched to resolve this issue. Users are advised to upgrade. There is no workaround for the underlying DOM Clobbering susceptibility. However, select plugins can be disabled on deployments which cannot update in a timely fashion to minimise the risk. These are: 1. @jupyterlab/mathjax-extension:plugin - users will loose ability to preview mathematical equations. 2. @jupyterlab/markdownviewer-extension:plugin - users will loose ability to open Markdown previews. 3. @jupyterlab/mathjax2-extension:plugin (if installed with optional jupyterlab-mathjax2 package) - an older version of the mathjax plugin for JupyterLab 4.x. To disable these extensions run: ``jupyter labextension disable @jupyterlab/markdownviewer-extension:plugin && jupyter labextension disable @jupyterlab/mathjax-extension:plugin && jupyter labextension disable @jupyterlab/mathjax2-extension:plugin `` in bash.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

DOM Clobbering in JupyterLab's Markdown preview allows XSS, granting access to victim's data and actions.

Vulnerability

Overview

JupyterLab (and Jupyter Notebook) contain a DOM Clobbering vulnerability in the Markdown preview and Markdown cell rendering features. The root cause is that the HTML sanitizer used by JupyterLab previously allowed id and name attributes in sanitized output. By crafting Markdown with elements that define these attributes, an attacker can overwrite references to global DOM properties, enabling cross-site scripting (XSS) [1][3][4].

Exploitation

An attacker can embed malicious HTML in a Markdown cell of a notebook or a Markdown file viewed via the JupyterLab preview feature. No authentication is required beyond what the victim has; the attack simply requires the victim to open the crafted file. The attack does not need any special network position beyond serving the malicious file to the target [1].

Impact

Successful exploitation allows the attacker to execute arbitrary JavaScript in the context of the victim's JupyterLab session. This enables access to any data the victim can access (notebooks, files, API tokens) and allows performing arbitrary requests as the victim, effectively a full session takeover [1].

Mitigation

Patches are available in JupyterLab v3.6.8, v4.2.5, and Jupyter Notebook v7.2.2 [1]. For deployments that cannot update immediately, administrators can disable the vulnerable plugins (@jupyterlab/mathjax-extension:plugin, @jupyterlab/markdownviewer-extension:plugin, and optionally @jupyterlab/mathjax2-extension:plugin) via the jupyter labextension disable command, at the cost of losing mathematical equation previews and Markdown preview functionality [1]. The fix itself modifies the Sanitizer class to conditionally allow id and name attributes only when explicitly enabled, preventing DOM Clobbering [3][4].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
jupyterlabPyPI
< 3.6.83.6.8
notebookPyPI
>= 7.0.0, < 7.2.27.2.2
jupyterlabPyPI
>= 4.0.0, < 4.2.54.2.5

Affected products

10

Patches

2
06ad9de836f1

Merge commit from fork

https://github.com/jupyterlab/jupyterlabMichał KrassowskiAug 26, 2024via ghsa
3 files changed · +35 12
  • packages/apputils-extension/schema/sanitizer.json+6 0 modified
    @@ -13,6 +13,12 @@
             "type": "string"
           },
           "default": ["http", "https", "ftp", "mailto", "tel"]
    +    },
    +    "allowNamedProperties": {
    +      "type": "boolean",
    +      "title": "Allow named properties",
    +      "description": "Whether to allow untrusted elements to include `name` and `id` attributes. These attributes are stripped by default to prevent DOM clobbering attacks.",
    +      "default": false
         }
       },
       "type": "object"
    
  • packages/apputils-extension/src/index.ts+4 0 modified
    @@ -619,10 +619,14 @@ const sanitizer: JupyterFrontEndPlugin<ISanitizer> = {
           const allowedSchemes = setting.get('allowedSchemes').composite as Array<
             string
           >;
    +      const allowNamedProperties = setting.get('allowNamedProperties')
    +        .composite as boolean;
     
           if (allowedSchemes) {
             sanitizer.setAllowedSchemes(allowedSchemes);
           }
    +
    +      sanitizer.setAllowNamedProperties(allowNamedProperties);
         };
     
         // Wait for the application to be restored and
    
  • packages/apputils/src/sanitizer.ts+25 12 modified
    @@ -435,6 +435,9 @@ class CssProp {
      * A class to sanitize HTML strings.
      */
     export class Sanitizer implements ISanitizer {
    +  constructor() {
    +    this._options = this._generateOptions();
    +  }
       /**
        * Sanitize an HTML string.
        *
    @@ -458,7 +461,17 @@ export class Sanitizer implements ISanitizer {
         this._options.allowedSchemes = [...scheme];
       }
     
    -  private _options: sanitize.IOptions = {
    +  /**
    +   * Set the whether to allow `name` and `id` attributes.
    +   */
    +  setAllowNamedProperties(allowNamedProperties: boolean): void {
    +    this._allowNamedProperties = allowNamedProperties;
    +    this._options = this._generateOptions();
    +  }
    +
    +  private _allowNamedProperties: boolean = false;
    +  private _options: sanitize.IOptions;
    +  private _generateOptions = (): sanitize.IOptions => ({
         // HTML tags that are allowed to be used. Tags were extracted from Google Caja
         allowedTags: [
           'a',
    @@ -573,7 +586,7 @@ export class Sanitizer implements ISanitizer {
             'dir',
             'draggable',
             'hidden',
    -        'id',
    +        ...(this._allowNamedProperties ? ['id'] : []),
             'inert',
             'itemprop',
             'itemref',
    @@ -590,7 +603,7 @@ export class Sanitizer implements ISanitizer {
             'coords',
             'href',
             'hreflang',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'rel',
             'shape',
             'tabindex',
    @@ -624,7 +637,7 @@ export class Sanitizer implements ISanitizer {
             'data-commandlinker-args',
             'data-commandlinker-command',
             'disabled',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'tabindex',
             'type',
             'value'
    @@ -655,7 +668,7 @@ export class Sanitizer implements ISanitizer {
             'autocomplete',
             'enctype',
             'method',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'novalidate'
           ],
           h1: ['align'],
    @@ -680,7 +693,7 @@ export class Sanitizer implements ISanitizer {
             'height',
             'hspace',
             'ismap',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'src',
             'usemap',
             'vspace',
    @@ -701,7 +714,7 @@ export class Sanitizer implements ISanitizer {
             'maxlength',
             'min',
             'multiple',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'placeholder',
             'readonly',
             'required',
    @@ -717,13 +730,13 @@ export class Sanitizer implements ISanitizer {
           label: ['accesskey', 'for'],
           legend: ['accesskey', 'align'],
           li: ['type', 'value'],
    -      map: ['name'],
    +      map: this._allowNamedProperties ? ['name'] : [],
           menu: ['compact', 'label', 'type'],
           meter: ['high', 'low', 'max', 'min', 'value'],
           ol: ['compact', 'reversed', 'start', 'type'],
           optgroup: ['disabled', 'label'],
           option: ['disabled', 'label', 'selected', 'value'],
    -      output: ['for', 'name'],
    +      output: ['for', ...(this._allowNamedProperties ? ['name'] : [])],
           p: ['align'],
           pre: ['width'],
           progress: ['max', 'min', 'value'],
    @@ -732,7 +745,7 @@ export class Sanitizer implements ISanitizer {
             'autocomplete',
             'disabled',
             'multiple',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'required',
             'size',
             'tabindex'
    @@ -772,7 +785,7 @@ export class Sanitizer implements ISanitizer {
             'cols',
             'disabled',
             'inputmode',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'placeholder',
             'readonly',
             'required',
    @@ -965,7 +978,7 @@ export class Sanitizer implements ISanitizer {
         // Since embedded data is no longer deemed to be a threat, validation can be skipped.
         // See https://github.com/jupyterlab/jupyterlab/issues/5183
         allowedSchemesAppliedToAttributes: ['href', 'cite']
    -  };
    +  });
     }
     
     /**
    
88e24baac551

Merge commit from fork

https://github.com/jupyterlab/jupyterlabMichał KrassowskiAug 26, 2024via ghsa
3 files changed · +34 13
  • packages/apputils-extension/schema/sanitizer.json+6 0 modified
    @@ -19,6 +19,12 @@
           "title": "Autolink URL replacement",
           "description": "Whether to replace URLs with links or not.",
           "default": true
    +    },
    +    "allowNamedProperties": {
    +      "type": "boolean",
    +      "title": "Allow named properties",
    +      "description": "Whether to allow untrusted elements to include `name` and `id` attributes. These attributes are stripped by default to prevent DOM clobbering attacks.",
    +      "default": false
         }
       },
       "type": "object"
    
  • packages/apputils-extension/src/index.ts+3 0 modified
    @@ -693,12 +693,15 @@ const sanitizer: JupyterFrontEndPlugin<IRenderMime.ISanitizer> = {
             .composite as Array<string>;
     
           const autolink = setting.get('autolink').composite as boolean;
    +      const allowNamedProperties = setting.get('allowNamedProperties')
    +        .composite as boolean;
     
           if (allowedSchemes) {
             sanitizer.setAllowedSchemes(allowedSchemes);
           }
     
           sanitizer.setAutolink(autolink);
    +      sanitizer.setAllowNamedProperties(allowNamedProperties);
         };
     
         // Wait for the application to be restored and
    
  • packages/apputils/src/sanitizer.ts+25 13 modified
    @@ -434,6 +434,9 @@ class CssProp {
      * A class to sanitize HTML strings.
      */
     export class Sanitizer implements IRenderMime.ISanitizer {
    +  constructor() {
    +    this._options = this._generateOptions();
    +  }
       /**
        * Sanitize an HTML string.
        *
    @@ -473,9 +476,18 @@ export class Sanitizer implements IRenderMime.ISanitizer {
         this._autolink = autolink;
       }
     
    -  private _autolink: boolean = true;
    +  /**
    +   * Set the whether to allow `name` and `id` attributes.
    +   */
    +  setAllowNamedProperties(allowNamedProperties: boolean): void {
    +    this._allowNamedProperties = allowNamedProperties;
    +    this._options = this._generateOptions();
    +  }
     
    -  private _options: sanitize.IOptions = {
    +  private _autolink: boolean = true;
    +  private _allowNamedProperties: boolean = false;
    +  private _options: sanitize.IOptions;
    +  private _generateOptions = (): sanitize.IOptions => ({
         // HTML tags that are allowed to be used. Tags were extracted from Google Caja
         allowedTags: [
           'a',
    @@ -590,7 +602,7 @@ export class Sanitizer implements IRenderMime.ISanitizer {
             'dir',
             'draggable',
             'hidden',
    -        'id',
    +        ...(this._allowNamedProperties ? ['id'] : []),
             'inert',
             'itemprop',
             'itemref',
    @@ -607,7 +619,7 @@ export class Sanitizer implements IRenderMime.ISanitizer {
             'coords',
             'href',
             'hreflang',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'rel',
             'shape',
             'tabindex',
    @@ -641,7 +653,7 @@ export class Sanitizer implements IRenderMime.ISanitizer {
             'data-commandlinker-args',
             'data-commandlinker-command',
             'disabled',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'tabindex',
             'type',
             'value'
    @@ -672,7 +684,7 @@ export class Sanitizer implements IRenderMime.ISanitizer {
             'autocomplete',
             'enctype',
             'method',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'novalidate'
           ],
           h1: ['align'],
    @@ -697,7 +709,7 @@ export class Sanitizer implements IRenderMime.ISanitizer {
             'height',
             'hspace',
             'ismap',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'src',
             'usemap',
             'vspace',
    @@ -718,7 +730,7 @@ export class Sanitizer implements IRenderMime.ISanitizer {
             'maxlength',
             'min',
             'multiple',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'placeholder',
             'readonly',
             'required',
    @@ -734,13 +746,13 @@ export class Sanitizer implements IRenderMime.ISanitizer {
           label: ['accesskey', 'for'],
           legend: ['accesskey', 'align'],
           li: ['type', 'value'],
    -      map: ['name'],
    +      map: this._allowNamedProperties ? ['name'] : [],
           menu: ['compact', 'label', 'type'],
           meter: ['high', 'low', 'max', 'min', 'value'],
           ol: ['compact', 'reversed', 'start', 'type'],
           optgroup: ['disabled', 'label'],
           option: ['disabled', 'label', 'selected', 'value'],
    -      output: ['for', 'name'],
    +      output: ['for', ...(this._allowNamedProperties ? ['name'] : [])],
           p: ['align'],
           pre: ['width'],
           progress: ['max', 'min', 'value'],
    @@ -749,7 +761,7 @@ export class Sanitizer implements IRenderMime.ISanitizer {
             'autocomplete',
             'disabled',
             'multiple',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'required',
             'size',
             'tabindex'
    @@ -789,7 +801,7 @@ export class Sanitizer implements IRenderMime.ISanitizer {
             'cols',
             'disabled',
             'inputmode',
    -        'name',
    +        ...(this._allowNamedProperties ? ['name'] : []),
             'placeholder',
             'readonly',
             'required',
    @@ -982,5 +994,5 @@ export class Sanitizer implements IRenderMime.ISanitizer {
         // Since embedded data is no longer deemed to be a threat, validation can be skipped.
         // See https://github.com/jupyterlab/jupyterlab/issues/5183
         allowedSchemesAppliedToAttributes: ['href', 'cite']
    -  };
    +  });
     }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.