Jul 23, 2026·5 min read·3 visits
A DOM-based Cross-Site Scripting (XSS) vulnerability in JupyterLab allows unauthenticated attackers to execute arbitrary JavaScript in a victim's session via a crafted 'overrides.json' file, potentially leading to remote command execution on the Jupyter backend.
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.
JupyterLab is an extensible, interactive development environment for Jupyter notebooks, code, and data. In version 4.5 and above, JupyterLab introduced capabilities to share and apply settings overrides via an overrides.json file. This is commonly performed using the Import button in the Settings Editor or by reading configuration files from specific system-wide paths during application startup.
The vulnerability, tracked as GHSA-pppj-hq3g-57pj, belongs to the DOM-based Cross-Site Scripting (XSS) category. The application fails to sanitize CSS configuration options before rendering them inside the DOM. This oversight allows a malicious settings file to execute arbitrary JavaScript within the context of the user's active session.
Because settings files are often perceived as non-executable data, they represent a low-suspicion attack vector. An attacker can leverage this trust to compromise environments where users frequently import configurations. Alternatively, on shared multi-tenant filesystems, an attacker can plant the file to compromise other users sharing the same server instance.
The root cause lies in how JupyterLab processes side-by-side notebook display options. Specifically, the configuration settings sideBySideLeftMarginOverride and sideBySideRightMarginOverride are defined as unconstrained string values in the tracker schema (packages/notebook-extension/schema/tracker.json).
When JupyterLab initializes the notebook tracker via the activateNotebookHandler function in packages/notebook-extension/src/index.ts, it retrieves these margin configurations. The application then uses string template literals to construct a raw CSS stylesheet block dynamically. The unchecked string values are concatenated directly into the CSS rule-set.
To inject the style into the document, the application uses the browser's document.head.insertAdjacentHTML('beforeend', ...) API. This method interprets the input string as raw HTML markup. Because the margin strings are not sanitized, escaping or filtering characters like < or > allows an attacker to terminate the <style> context and execute arbitrary HTML elements, including <script> blocks.
A review of the fix in commit be9303f5bcd5308eaeae953c5a3c903046682c2c and f1beab4a2027af4719d6edc07d52d6cf5a39a432 reveals two primary defensive changes: strict schema validation and safe DOM manipulation.
In the schema file packages/notebook-extension/schema/tracker.json, the string type was reinforced with a strict regular expression validation pattern:
"pattern": "^-?(?:0|(?:[0-9]+|[0-9]*\\.[0-9]+)(?:px|em|rem|%|vw|vh|vmin|vmax|ch|ex|cm|mm|in|pt|pc))$"This pattern limits inputs strictly to valid CSS dimension units. It blocks special characters such as <, >, ;, or curly braces, making injection impossible via standard schema-validated paths.
Additionally, in packages/notebook-extension/src/index.ts, the dangerous DOM sink was replaced with programmatic element creation:
// Patched implementation
const style = document.createElement('style');
style.id = SIDE_BY_SIDE_STYLE_ID;
style.textContent = sideBySideMarginStyle;
document.head.appendChild(style);Using textContent rather than insertAdjacentHTML forces the browser to treat the input string as raw text rather than executable markup. Even if schema validation is bypassed or disabled, the browser will not parse or execute nested HTML tags inside the stylesheet.
The attack requires the delivery of a malicious overrides.json file. An attacker can construct this file with the payload positioned inside the sideBySideLeftMarginOverride parameter.
{
"@jupyterlab/notebook-extension:tracker": {
"sideBySideLeftMarginOverride": "10px; }</style><script>console.log(window.location.origin);</script><style>",
"sideBySideRightMarginOverride": "10px"
}
}When a victim loads this file via the Settings Editor import function, or if the file is loaded automatically from an overridden configuration path, the style generation routine executes. The browser processes the injected </style> tag, terminating the stylesheet definition immediately, and then executes the nested <script> block.
Below is the sequential logic flow of the exploit:
Once JavaScript execution is achieved inside the JupyterLab origin, the payload can access the JupyterLab API. It can then request files, execute arbitrary terminal commands through the server backend, or run malicious code in active notebook kernels.
The security impact of this vulnerability is high. Because the JavaScript runs inside the authenticated session of the JupyterLab user, the code can perform any action the user is authorized to execute. This includes reading, writing, or deleting notebooks, environment variables, and local data files.
Furthermore, because JupyterLab runs a local notebook server capable of spawning system kernels, the script can issue API commands to create or use existing kernels. This allows the attacker to execute arbitrary OS commands on the underlying server, escalating a client-side XSS to an unauthenticated remote code execution (RCE) condition on the host.
The CVSS v4.0 base score is calculated at 8.6 (High), reflecting the lack of required privileges for the social-engineering vector, and the high impact on both confidentiality and integrity of the user's environment. The vulnerability represents a severe threat in multi-tenant environments or academic compute clusters where notebooks are routinely shared among users.
The principal defense against this vulnerability is updating JupyterLab to the patched versions. Users running the 4.6.x branch must update to version 4.6.2 or later. Users running the older 3.3.0 through 4.5.x branches must update to 4.5.10 or later.
Where immediate updates are not feasible, organizations should employ several hardening measures. First, enforce strict filesystem permissions on shared Jupyter environments. Ensure that standard users cannot write to system-wide configuration directories where overrides.json might be processed automatically during another user's startup.
Second, educate users about the risks of importing untrusted configuration files. Configuration files must be audited with the same diligence as executable scripts. Lastly, implement Content Security Policies (CSP) that restrict inline script execution to reduce the impact of DOM-based XSS vulnerabilities across the platform.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:L/SC:N/SI:N/SA:L| Product | Affected Versions | Fixed Version |
|---|---|---|
jupyterlab Jupyter | >= 4.6.0, <= 4.6.1 | 4.6.2 |
jupyterlab Jupyter | >= 3.3.0, <= 4.5.9 | 4.5.10 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS v4.0 | 8.6 (High) |
| Impact | Arbitrary JavaScript Execution / RCE via Kernels |
| Exploit Status | PoC Conceptual |
| KEV Status | Not Listed |
The software does not neutralize or incorrectly neutralizes user-controlled input before it is placed in output that is used as a web page, which is then parsed and executed by a web browser.
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.
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'.
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.
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.
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.
Eclipse Jetty is subject to an uncontrolled resource consumption vulnerability in its HTTP connection handling component. When processing certain HTTP request sequences, such as those invoking the Expect: 100-Continue handshake under specific network constraints, the server fails to return allocated buffers to its central pool. Over time, this leads to buffer pool exhaustion and a complete denial of service via memory starvation.