GHSA-VMHF-C436-HXJ4
Vulnerability from github – Published: 2026-06-19 15:11 – Updated: 2026-06-19 15:11A malicious PyPI package can place a javascript: URL in its [project.urls] metadata. JupyterLab's Extension Manager renders this as the extension's home-page link without validating the protocol, so a user who clicks the extension name executes attacker-controlled JavaScript in the JupyterLab origin.
Details
One of the PyPI package's URL (jupyterlab/extensions/pypi.py) is copied straight into the homepage_url rendered by the frontend in packages/extensionmanager/src/widget.tsx#L77-L88.
best_guess_home_url = (
homepage_url # home_page / [project.urls] Homepage
or data.get("project_url")
or data.get("package_url")
or documentation_url # docs_url / [project.urls] Documentation
or source_url # [project.urls] Source Code
or bug_tracker_url # bugtrack_url / [project.urls] Bug Tracker
)
# homepage_url=best_guess_home_url
{entry.homepage_url ? (
<a href={entry.homepage_url} target="_blank" rel="noopener noreferrer" ...>
{entry.name}
</a>
) : ( <div>{entry.name}</div> )}
Impact
An attacker needs to publish a package to PyPI (no access to the target). When the package appears in a victim's extension manager list and the victim clicks the extension name, the payload runs in the JupyterLab origin.
Preconditions: Extension Manager enabled with the default PyPI source, the malicious package appears in the victim's list/search results.
Patches
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.5.8"
},
"package": {
"ecosystem": "PyPI",
"name": "jupyterlab"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.5.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-84"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-19T15:11:17Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "A malicious PyPI package can place a `javascript:` URL in its `[project.urls]` metadata. JupyterLab\u0027s Extension Manager renders this as the extension\u0027s home-page link without validating the protocol, so a user who clicks the extension name executes attacker-controlled JavaScript in the JupyterLab origin.\n\n### Details\n\nOne of the PyPI package\u0027s URL (jupyterlab/extensions/pypi.py) is copied straight into the `homepage_url` rendered by the frontend in packages/extensionmanager/src/widget.tsx#L77-L88.\n\n```python\nbest_guess_home_url = (\n homepage_url # home_page / [project.urls] Homepage\n or data.get(\"project_url\")\n or data.get(\"package_url\")\n or documentation_url # docs_url / [project.urls] Documentation\n or source_url # [project.urls] Source Code\n or bug_tracker_url # bugtrack_url / [project.urls] Bug Tracker\n)\n\n# homepage_url=best_guess_home_url\n```\n\n```tsx\n{entry.homepage_url ? (\n \u003ca href={entry.homepage_url} target=\"_blank\" rel=\"noopener noreferrer\" ...\u003e\n {entry.name}\n \u003c/a\u003e\n) : ( \u003cdiv\u003e{entry.name}\u003c/div\u003e )}\n```\n\n### Impact\n\nAn attacker needs to publish a package to PyPI (no access to the target). When the package appears in a victim\u0027s extension manager list and the victim clicks the extension name, the payload runs in the JupyterLab origin.\n\nPreconditions: Extension Manager enabled with the default PyPI source, the malicious package appears in the victim\u0027s list/search results.\n\n### Patches\nPatched in [4.5.9](https://github.com/jupyterlab/jupyterlab/releases/tag/v4.5.9), commits [4e61e07](https://github.com/jupyterlab/jupyterlab/commit/4e61e07d0a91145b53fbf96ac74b0387f6bc51f6) and [d5d961f](https://github.com/jupyterlab/jupyterlab/commit/d5d961f6e10a6442dddbf94d9a976b3897055a12)",
"id": "GHSA-vmhf-c436-hxj4",
"modified": "2026-06-19T15:11:17Z",
"published": "2026-06-19T15:11:17Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jupyterlab/jupyterlab/security/advisories/GHSA-vmhf-c436-hxj4"
},
{
"type": "WEB",
"url": "https://github.com/jupyterlab/jupyterlab/commit/4e61e07d0a91145b53fbf96ac74b0387f6bc51f6"
},
{
"type": "WEB",
"url": "https://github.com/jupyterlab/jupyterlab/commit/d5d961f6e10a6442dddbf94d9a976b3897055a12"
},
{
"type": "PACKAGE",
"url": "https://github.com/jupyterlab/jupyterlab"
},
{
"type": "WEB",
"url": "https://github.com/jupyterlab/jupyterlab/releases/tag/v4.5.9"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N",
"type": "CVSS_V4"
}
],
"summary": "JupyterLab: Stored XSS in extension manager through package metadata unsanitized URI protocol"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.