GHSA-whvh-wf3x-g77j: JupyterLab: missing await skips extension allowlist check
GHSA-whvh-wf3x-g77j LOWA missing `await` in JupyterLab's `PyPIExtensionManager.install()` means the allowlist/blocklist check silently never runs, so calls to this method don't actually enforce package restrictions — the only visible symptom was a RuntimeWarning in logs, not a crash. This is a low-severity defense-in-depth gap, not a directly exploitable flaw in stock JupyterLab: the HTTP API and Extension Manager UI perform their own separate, correctly-awaited check before ever calling `install()`, so out-of-the-box deployments are unaffected. It only matters for organizations that built custom extensions or downstream integrations calling `install()` directly with user-influenced package names, and that deliberately disabled kernels/terminals to force all package installation through that one gate — a pattern seen in locked-down, multi-tenant JupyterHub-style AI/ML notebook platforms. There is no known exploit code, it's not in CISA KEV, and EPSS data is unavailable, consistent with its low real-world exploitation likelihood. Action: upgrade to JupyterLab v4.6.2 / v4.5.10 (and update `notebook` if running v7+, since it depends on jupyterlab), or set `c.LabApp.extension_manager = 'readonly'` to disable programmatic installs entirely if custom `install()` call sites can't be updated immediately.
What is the risk?
Low. The CVSS 3.1 vector (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:N) reflects zero confidentiality/integrity/availability impact on its own — this is purely a control-bypass bug whose real-world consequence depends entirely on what a downstream deployer built on top of `install()`. Stock JupyterLab is not exposed because the HTTP API path performs its own independent, correctly-awaited allowlist check. Exploitability requires a fairly specific and unusual configuration: a custom extension calling `install()` directly with attacker-influenced input, an allowlist/blocklist actually configured with security intent, and kernels/terminals disabled so that this is the only install vector available (otherwise a user with kernel access could just install packages directly, making the check moot anyway). No public exploit, no Nuclei template, not in CISA KEV — this is a quiet, narrow-scope fix rather than an urgent one.
How does the attack unfold?
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| Jupyter | pip | >= 4.6.0, <= 4.6.1 | 4.6.2 |
| Jupyter Notebook | pip | — | No patch |
How severe is it?
What is the attack surface?
What should I do?
1 step-
1) Upgrade to JupyterLab v4.6.2 or v4.5.10; if running Notebook v7+ (which depends on jupyterlab), update that package too. 2) If immediate upgrade isn't possible and programmatic extension installation isn't needed, set
c.LabApp.extension_manager = 'readonly'(or--LabApp.extension_manager=readonly) to disable install-capable extension management entirely — confirm via the GUI that the read-only manager is active. 3) Audit any internal/custom extensions or integrations that callPyPIExtensionManager.install()directly; do not assume it self-enforces an allowlist — perform the allow/block check explicitly in the calling code as well. 4) Detection: the only runtime symptom pre-patch isRuntimeWarning: coroutine 'is_install_allowed' was never awaitedin logs — grep application/server logs for this string to identify still-vulnerable, unpatched instances actively hitting the code path.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is GHSA-whvh-wf3x-g77j?
A missing `await` in JupyterLab's `PyPIExtensionManager.install()` means the allowlist/blocklist check silently never runs, so calls to this method don't actually enforce package restrictions — the only visible symptom was a RuntimeWarning in logs, not a crash. This is a low-severity defense-in-depth gap, not a directly exploitable flaw in stock JupyterLab: the HTTP API and Extension Manager UI perform their own separate, correctly-awaited check before ever calling `install()`, so out-of-the-box deployments are unaffected. It only matters for organizations that built custom extensions or downstream integrations calling `install()` directly with user-influenced package names, and that deliberately disabled kernels/terminals to force all package installation through that one gate — a pattern seen in locked-down, multi-tenant JupyterHub-style AI/ML notebook platforms. There is no known exploit code, it's not in CISA KEV, and EPSS data is unavailable, consistent with its low real-world exploitation likelihood. Action: upgrade to JupyterLab v4.6.2 / v4.5.10 (and update `notebook` if running v7+, since it depends on jupyterlab), or set `c.LabApp.extension_manager = 'readonly'` to disable programmatic installs entirely if custom `install()` call sites can't be updated immediately.
Is GHSA-whvh-wf3x-g77j actively exploited?
No confirmed active exploitation of GHSA-whvh-wf3x-g77j has been reported, but organizations should still patch proactively.
How to fix GHSA-whvh-wf3x-g77j?
1) Upgrade to JupyterLab v4.6.2 or v4.5.10; if running Notebook v7+ (which depends on jupyterlab), update that package too. 2) If immediate upgrade isn't possible and programmatic extension installation isn't needed, set `c.LabApp.extension_manager = 'readonly'` (or `--LabApp.extension_manager=readonly`) to disable install-capable extension management entirely — confirm via the GUI that the read-only manager is active. 3) Audit any internal/custom extensions or integrations that call `PyPIExtensionManager.install()` directly; do not assume it self-enforces an allowlist — perform the allow/block check explicitly in the calling code as well. 4) Detection: the only runtime symptom pre-patch is `RuntimeWarning: coroutine 'is_install_allowed' was never awaited` in logs — grep application/server logs for this string to identify still-vulnerable, unpatched instances actively hitting the code path.
What systems are affected by GHSA-whvh-wf3x-g77j?
This vulnerability affects the following AI/ML architecture patterns: notebook-based ML development environments, multi-tenant AI development platforms (JupyterHub-style), training pipelines.
What is the CVSS score for GHSA-whvh-wf3x-g77j?
No CVSS score has been assigned yet.
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0010.001 AI Software AML.T0011.001 Malicious Package Compliance Controls Affected
What are the technical details?
Original Advisory
The extension allowlist/blocklist check inside `PyPIExtensionManager.install()` was not enforced due to a missing await. For purposes of JupyterLab this was a secondary defense-in-depth check: `install()` was intended to enforce the allowlist/blocklist itself for any future uses and users calling this method directly (in addition to the separate check handling requests arriving through the HTTP API). The only runtime symptom was a `RuntimeWarning: coroutine 'is_install_allowed' was never awaited.` This has security implications only for deployments that combine all of the following: - a custom extension or downstream integration that imports `PyPIExtensionManager` and calls `install()` directly with a package name influenced by untrusted user input (the stock JupyterLab HTTP handler is not affected - it performs its own awaited allowlist check before calling `install()`); - an allowlist/blocklist configured with the intent of restricting which packages users can install; - the (default) PyPI Extension Manager enabled; and - kernels and terminals disabled or delegated to remote hosts, so that the custom extension's `install()` call is the only available package-install vector (otherwise a user with kernel access can install packages directly regardless of this check) ### Impact Low. No exposure for stock JupyterLab: the HTTP API and Extension Manager UI enforce the listing through a separate, correctly awaited check. The gap affected only custom extensions or downstream integrations that called the public `install()` method directly and relied on it to self-enforce. ### Patches JupyterLab [`v4.6.2`](https://github.com/jupyterlab/jupyterlab/releases/tag/v4.6.2) and [`v4.5.10`](https://github.com/jupyterlab/jupyterlab/releases/tag/v4.5.10) contain the patch. Users of applications that depend on JupyterLab, such as Notebook v7+, should update `jupyterlab` package too. ### Workarounds No action is required for deployments that only expose extension management through the JupyterLab HTTP API / Extension Manager UI, as that path was already enforcing the listing via the handler's own check. Deployments wanting to disable programmatic extension installation entirely can switch to the read-only extension manager: ```bash --LabApp.extension_manager=readonly ``` or the following traitlet: ```python c.LabApp.extension_manager = 'readonly' ``` You can confirm that the read-only manager is in use from GUI: <img width="293" height="293" alt="image" src="https://github.com/user-attachments/assets/8016c809-633e-4ed0-a5bc-6bc4793caa0f" />
Exploitation Scenario
An organization runs a shared JupyterHub-based ML training platform where, for security, admins disabled direct kernel and terminal access for data scientists and instead built a custom extension that lets users request approved packages, calling `PyPIExtensionManager.install()` directly and trusting it to enforce a configured package blocklist. A user (or an attacker who has obtained low-privilege notebook access) requests installation of a blocklisted or unapproved package — for example a typosquatted or supply-chain-compromised PyPI package — through this extension. Because the allowlist check coroutine is never awaited, it never actually runs, so the block silently fails open and the package installs anyway. The malicious package's install-time code (or code imported afterward) executes within the shared notebook kernel, giving the attacker code execution inside the ML training environment and a foothold to pivot to datasets, credentials, or other tenants' notebooks on the same platform.
Weaknesses (CWE)
CWE-284 — Improper Access Control: The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.
- [Architecture and Design, Operation] Very carefully manage the setting, management, and handling of privileges. Explicitly manage trust zones in the software.
- [Architecture and Design] Compartmentalize the system to have "safe" areas where trust boundaries can be unambiguously drawn. Do not allow sensitive data to go outside of the trust boundary and always be careful when interfacing with a compartment outside of the safe area. Ensure that appropriate compartmentalization is built into the system design, and the compartmentalization allows for and reinforces privilege separation functionality. Architects and designers should rely on the principle of least privilege to decide the appropriate time to use privileges and the time to drop privileges.
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:N References
- github.com/advisories/GHSA-whvh-wf3x-g77j
- github.com/jupyterlab/jupyterlab/commit/be9303f5bcd5308eaeae953c5a3c903046682c2c
- github.com/jupyterlab/jupyterlab/commit/f1beab4a2027af4719d6edc07d52d6cf5a39a432
- github.com/jupyterlab/jupyterlab/pull/19184
- github.com/jupyterlab/jupyterlab/pull/19185
- github.com/jupyterlab/jupyterlab/pull/19186
- github.com/jupyterlab/jupyterlab/security/advisories/GHSA-whvh-wf3x-g77j
Timeline
Related Vulnerabilities
CVE-2023-25574 10.0 JupyterHub LTI13: JWT forgery enables full auth bypass
Same package: jupyter CVE-2026-44180 9.8 Jupyter Enterprise Gateway: root privilege bypass in Kubernetes
Same package: jupyter CVE-2026-23537 9.1 Feast: unauth file write to RCE via /save-document
Same package: jupyter CVE-2026-54527 9.0 jupyterlab-git: stored XSS escalates to full RCE
Same package: jupyter CVE-2026-44727 9.0 jupyter-server: stored XSS yields kernel RCE
Same package: jupyter