GHSA-CJ5W-8MJF-R5F8

Vulnerability from github – Published: 2025-04-04 14:05 – Updated: 2025-04-04 14:05
VLAI?
Summary
jupyterlab-git has a command injection vulnerability in "Open Git Repository in Terminal"
Details

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

  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 ..
  1. Start JupyterLab from test/ by running jupyter lab.
  2. With JupyterLab open in the browser, double click on $(touch pwned.txt) in the file browser.
  3. From the top menu bar, click "Git > Open Git Repository in Terminal".
  4. 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.

Patch (click to expand)
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;

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

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "jupyterlab-git"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.51.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-30370"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-78"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-04-04T14:05:42Z",
    "nvd_published_at": "2025-04-03T22:15:21Z",
    "severity": "HIGH"
  },
  "details": "## Overview\n\nOn many platforms, a third party can create a Git repository under a name that includes a shell command substitution [^1] string in the syntax `$(\u003ccommand\u003e)`. 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 \u003e Open Git Repository in Terminal\" from the menu bar, then the injected command `\u003ccommand\u003e` is run in the user\u0027s shell without the user\u0027s permission.\n\nThis issue is occurring because when that menu entry is clicked, `jupyterlab-git` opens the terminal and runs `cd \u003cgit-repo-path\u003e` 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].\n\n[^1]: https://www.gnu.org/software/bash/manual/html_node/Command-Substitution.html\n[^2]: https://www.gnu.org/software/libc/manual/html_node/File-Name-Portability.html\n[^3]: https://github.com/jupyterlab/jupyterlab-git/blob/7eb3b06f0092223bd5494688ec264527bbeb2195/src/commandsAndMenu.tsx#L175-L184\n[^4]: https://github.com/jupyterlab/jupyterlab-git/pull/1196\n \n\n## Scope of Impact\n\nThis 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\u0027s security rules.\n\nWe have scanned the source code of `jupyterlab-git` for other command injection risks, and have not found any at the time of writing.\n\nThis 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.\n \n\n## Proof-of-concept\n\n1. Create a new directory via `mkdir test/ \u0026\u0026 cd test/`.\n\n2. Create a new Git repository under `test/` with a command substitution string in the directory name by running these commands:\n\n```\nmkdir \u0027$(touch pwned.txt)\u0027\ncd \u0027$(touch pwned.txt)/\u0027\ngit init\ncd ..\n```\n\n3. Start JupyterLab from `test/` by running jupyter lab.\n4. With JupyterLab open in the browser, double click on `$(touch pwned.txt)` in the file browser.\n5. From the top menu bar, click \"Git \u003e Open Git Repository in Terminal\".\n6. Verify that `pwned.txt` is created under `test/`. This demonstrates the command injection issue described here.\n\n## Proof-of-concept mitigation\n\nThe issue can be mitigated by the patch shown below.\n\n\u003cdetails\u003e\u003csummary\u003ePatch (click to expand)\u003c/summary\u003e\n\n```diff\ndiff --git a/src/commandsAndMenu.tsx b/src/commandsAndMenu.tsx\nindex 3779a6c..71ddcea 100644\n--- a/src/commandsAndMenu.tsx\n+++ b/src/commandsAndMenu.tsx\n@@ -164,31 +164,13 @@ export function addCommands(\n     label: trans.__(\u0027Open Git Repository in Terminal\u0027),\n     caption: trans.__(\u0027Open a New Terminal to the Git Repository\u0027),\n     execute: async args =\u003e {\n-      const main = (await commands.execute(\n-        \u0027terminal:create-new\u0027,\n-        args\n-      )) as MainAreaWidget\u003cITerminal.ITerminal\u003e;\n+      const cwd = gitModel.pathRepository;\n+      const main = (await commands.execute(\u0027terminal:create-new\u0027, {\n+        ...args,\n+        cwd\n+      })) as MainAreaWidget\u003cITerminal.ITerminal\u003e;\n \n-      try {\n-        if (gitModel.pathRepository !== null) {\n-          const terminal = main.content;\n-          terminal.session.send({\n-            type: \u0027stdin\u0027,\n-            content: [\n-              `cd \"${gitModel.pathRepository\n-                .split(\u0027\"\u0027)\n-                .join(\u0027\\\\\"\u0027)\n-                .split(\u0027`\u0027)\n-                .join(\u0027\\\\`\u0027)}\"\\n`\n-            ]\n-          });\n-        }\n-\n-        return main;\n-      } catch (e) {\n-        console.error(e);\n-        main.dispose();\n-      }\n+      return main;\n```\n\u003c/details\u003e\n\nThis patch removes the `cd \u003cgit-repo-path\u003e` shell command that causes the issue. To preserve the existing behavior, the `cwd` argument is set to `\u003cgit-repo-path\u003e` 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.\n\nWe 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.\n\n## Workarounds\n\nWe 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. \n\n1. Disable terminals on `jupyter-server` level:\n    ```\n    c.ServerApp.terminals_enabled =  False\n    ```\n\n2. Disable the terminals server extension:\n    ```\n    jupyter server extension disable jupyter_server_terminals\n    ```\n\n3. Disable the lab extension:\n    ```\n    jupyter labextension disable @jupyterlab/terminal-extension\n    ```",
  "id": "GHSA-cj5w-8mjf-r5f8",
  "modified": "2025-04-04T14:05:42Z",
  "published": "2025-04-04T14:05:42Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/jupyterlab/jupyterlab-git/security/advisories/GHSA-cj5w-8mjf-r5f8"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-30370"
    },
    {
      "type": "WEB",
      "url": "https://github.com/jupyterlab/jupyterlab-git/pull/1196"
    },
    {
      "type": "WEB",
      "url": "https://github.com/jupyterlab/jupyterlab-git/commit/b46482993f76d3a546015c6a94ebed8b77fc2376"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/jupyterlab/jupyterlab-git"
    },
    {
      "type": "WEB",
      "url": "https://github.com/jupyterlab/jupyterlab-git/blob/7eb3b06f0092223bd5494688ec264527bbeb2195/src/commandsAndMenu.tsx#L175-L184"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "jupyterlab-git has a command injection vulnerability in \"Open Git Repository in Terminal\""
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

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.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…