CVE-2025-30370 is a high-severity (CVSS 7.4) OS Command Injection vulnerability in jupyterlab-git. A fix is available for jupyterlab-git — see the affected versions and patch details below.
jupyterlab-git has a command injection vulnerability in "Open Git Repository in Terminal"
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for CVE-2025-30370.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
CVE-2025-30370 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 378,567 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
jupyterlab-gitReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
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.
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
-
Create a new directory via
mkdir test/ && cd test/. -
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 ..
- Start JupyterLab from
test/by running jupyter lab. - With JupyterLab open in the browser, double click on
$(touch pwned.txt)in the file browser. - From the top menu bar, click "Git > Open Git Repository in Terminal".
- Verify that
pwned.txtis created undertest/. 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 --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.
-
Disable terminals on
jupyter-serverlevel:c.ServerApp.terminals_enabled = False -
Disable the terminals server extension:
jupyter server extension disable jupyter_server_terminals -
Disable the lab extension:
jupyter labextension disable @jupyterlab/terminal-extension
Footnotes
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | jupyterlab-git | all versions | 0.51.1pip install --upgrade 'jupyterlab-git==0.51.1' |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for jupyterlab-git, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update jupyterlab-git to 0.51.1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2025-30370 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2025-30370 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2025-30370. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is CVE-2025-30370 in your dependencies?
O3 Security finds CVE-2025-30370 across PyPI dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.