# JupyterLab: Cross-site scripting (XSS) in JupyterLab via notebook cells pasted from the system clipboard

Pasted notebook cells from the system clipboard keep attacker-supplied metadata.trusted, so a code cell's HTML output runs script in the JupyterLab origin on paste.

- Payout: Research
- Severity: high
- Program: JupyterLab
- Found by: rexpository, krassowski
- Disclosed: 2026-10-01
- CVE: CVE-2026-102831
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-6966-vjj6-99xv
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-6966-vjj6-99xv
- Tags: area:os domain:desktop domain:web impact:integrity impact:rce kind:research platform:ghsa sev:high tech:python technique:source-review vuln:xss

**Target:** JupyterLab notebook cell paste path (system clipboard JSON parsing)
**Root cause:** The system-clipboard paste path parses clipboard text as cell JSON and inserts cells without clearing metadata.trusted, letting the payload declare its own output trusted. Trusted outputs are not sanitized, so embedded <script> elements are evaluated.
**Steps:**
1. Place the advisory's JSON array (code cell with `"metadata": { "trusted": true }` and a display_data output containing `text/html` `<script>...</script>`) on the system clipboard.
2. Ensure `useSystemClipboardForCells` is enabled (always on in 4.5.0/4.5.1) and `pasteCodeCellsWithoutOutput` is off (default).
3. Run a Paste Cells command (context menu, command palette, or shortcut) in a notebook.
4. The pasted cell's output is treated as trusted and the script executes in the JupyterLab origin; same payload with `"trusted": false` is sanitized.
**How found:** Not stated; advisory credits reporter rexpository without describing the discovery path.
**Exploitation:** Attacker gets the victim to copy attacker-controlled text (a page can replace copied text, or write the clipboard after a user gesture/permission), then the victim pastes cells into JupyterLab. The script runs in the authenticated user's origin and can call the Jupyter Server REST API to read/write files under the server root, start a kernel and run code, and open a terminal where enabled.
**Escalation:** From in-page JS to full server-side control via the Jupyter Server REST API (file read/write, kernel code execution, terminal).
**Insight:** When an app adds a system-clipboard import path, check whether imported objects carry trust/authenticity flags (metadata.trusted, signed markers) that the importer fails to strip — trust flags must be re-derived from origin, not read from the payload.

## Proof of concept (copied exactly from the original report)

**Malicious clipboard cell JSON with trusted output script**

```
[
  {
    "cell_type": "code",
    "execution_count": null,
    "source": "",
    "metadata": { "trusted": true },
    "outputs": [
      {
        "output_type": "display_data",
        "metadata": {},
        "data": { "text/html": "<script>/* runs on paste */</script>" }
      }
    ]
  }
]
```

