GHSA-V396-V7Q4-X2QJ

Vulnerability from github – Published: 2026-07-21 19:43 – Updated: 2026-07-21 19:43
VLAI
Summary
GitPython unsafe clone option gate bypass through joined short options
Details

GitPython version 3.1.50 blocks unsafe git clone options such as --upload-pack, -u, --config, and -c unless callers explicitly pass allow_unsafe_options=True. However, the default unsafe-option gate does not recognize joined short-option forms such as -u/path/to/helper.

Git itself accepts -u<upload-pack> as the short form of --upload-pack=<upload-pack>. As a result, Repo.clone_from(..., multi_options=["-u<helper>"], allow_unsafe_options=False) can execute the helper command even though the equivalent long option is blocked.

Affected package:

  • Ecosystem: PyPI
  • Package: GitPython
  • Confirmed affected version: 3.1.50
  • Repository: gitpython-developers/GitPython
  • Current PyPI version during triage: 3.1.50

Relevant behavior:

  • Repo.unsafe_git_clone_options correctly lists --upload-pack, -u, --config, and -c as unsafe clone options.
  • Repo._clone() splits multi_options with shlex.split(" ".join(multi_options)) and then calls Git.check_unsafe_options(...).
  • _canonicalize_option_name("-u/path/to/helper") returns a string beginning with u..., not the canonical short option u, so it does not match the blocked -u entry.
  • Git accepts the same joined short option as --upload-pack=<helper> and executes the helper during clone.

Preconditions:

An application must pass attacker-influenced clone options into Repo.clone_from(..., multi_options=...) while relying on GitPython's default unsafe-option gate to block command-executing options.

The local PoC uses only a local bare Git repository and a local helper script. It does not contact any third-party service.

Local reproduction:

The PoC creates a disposable bare Git repository, a helper script, and a sentinel file path. It first confirms that the long --upload-pack=<helper> form is blocked by GitPython. It then calls Repo.clone_from(..., multi_options=["-u<helper>"], allow_unsafe_options=False).

Observed sanitized output:

gitpython_version=3.1.50
git_version=git version 2.53.0.windows.1
tmp_dir=<tmp>
long_upload_pack_gate=BLOCKED:UnsafeOptionError
joined_short_upload_pack_gate=ALLOWED
clone_result=EXPECTED_EXCEPTION:GitCommandError
sentinel_exists=True
sentinel_text=GITPYTHON_UNSAFE_OPTION_BYPASS

The clone fails because the helper exits nonzero, but the sentinel file proves that Git executed the helper despite allow_unsafe_options=False.

Impact:

An attacker who controls multi_options can bypass GitPython's default allow_unsafe_options=False protection and execute a local command via Git's --upload-pack / -u clone option. This is a residual bypass of an explicit GitPython security boundary, not merely a case where a caller opted into unsafe behavior.

Duplicate / related advisory checks:

  • OSV query for PyPI/GitPython version 3.1.50 returned no vulnerabilities.
  • The repository's public advisories include related unsafe Git option issues, including GHSA-x2qx-6953-8485 / CVE-2026-42284 and GHSA-rpm5-65cw-6hj4 / CVE-2026-42215. Their public affected ranges are marked as fixed before 3.1.50.
  • GHSA-x2qx-6953-8485 describes validating multi_options before shlex.split(...). GitPython 3.1.50 now validates after splitting, but the joined short option -u<value> still bypasses because the validator canonicalizes it to u<value> rather than u.
  • GHSA-rpm5-65cw-6hj4 describes unsafe underscored kwargs such as upload_pack=.... The current PoC uses multi_options=["-u<helper>"] against 3.1.50 and does not depend on underscored kwargs.
  • GitHub issue search for upload-pack unsafe options found historical related items, including CVE-2022-24439 and the earlier unsafe-options gate work, but no public issue describing this current joined-short-option residual bypass in 3.1.50.
  • GitHub issue search for multi_options unsafe found PR #2130, which fixed splitting of multi_options before checking. The current issue remains after that split because -u<value> is treated as option name u<value>, not blocked short option u.
  • GitHub issue searches for u<upload-pack> unsafe and -cfoo returned no results.

Suggested remediation:

When checking unsafe Git options, parse joined short options that take values. For clone, -uVALUE and -cKEY=VALUE should be canonicalized to u and c respectively before comparing against the unsafe option set.

A safer approach is to maintain command-specific metadata for unsafe short options and recognize the bare option, split form, joined form, and long --option=<value> / --option <value> forms.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "GitPython"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "3.1.50"
            },
            {
              "fixed": "3.1.51"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ],
      "versions": [
        "3.1.50"
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-78"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-21T19:43:14Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "`GitPython` version `3.1.50` blocks unsafe `git clone` options such as `--upload-pack`, `-u`, `--config`, and `-c` unless callers explicitly pass `allow_unsafe_options=True`. However, the default unsafe-option gate does not recognize joined short-option forms such as `-u/path/to/helper`.\n\nGit itself accepts `-u\u003cupload-pack\u003e` as the short form of `--upload-pack=\u003cupload-pack\u003e`. As a result, `Repo.clone_from(..., multi_options=[\"-u\u003chelper\u003e\"], allow_unsafe_options=False)` can execute the helper command even though the equivalent long option is blocked.\n\nAffected package:\n\n- Ecosystem: PyPI\n- Package: `GitPython`\n- Confirmed affected version: `3.1.50`\n- Repository: `gitpython-developers/GitPython`\n- Current PyPI version during triage: `3.1.50`\n\nRelevant behavior:\n\n- `Repo.unsafe_git_clone_options` correctly lists `--upload-pack`, `-u`, `--config`, and `-c` as unsafe clone options.\n- `Repo._clone()` splits `multi_options` with `shlex.split(\" \".join(multi_options))` and then calls `Git.check_unsafe_options(...)`.\n- `_canonicalize_option_name(\"-u/path/to/helper\")` returns a string beginning with `u...`, not the canonical short option `u`, so it does not match the blocked `-u` entry.\n- Git accepts the same joined short option as `--upload-pack=\u003chelper\u003e` and executes the helper during clone.\n\nPreconditions:\n\nAn application must pass attacker-influenced clone options into `Repo.clone_from(..., multi_options=...)` while relying on GitPython\u0027s default unsafe-option gate to block command-executing options.\n\nThe local PoC uses only a local bare Git repository and a local helper script. It does not contact any third-party service.\n\nLocal reproduction:\n\nThe PoC creates a disposable bare Git repository, a helper script, and a sentinel file path. It first confirms that the long `--upload-pack=\u003chelper\u003e` form is blocked by GitPython. It then calls `Repo.clone_from(..., multi_options=[\"-u\u003chelper\u003e\"], allow_unsafe_options=False)`.\n\nObserved sanitized output:\n\n```text\ngitpython_version=3.1.50\ngit_version=git version 2.53.0.windows.1\ntmp_dir=\u003ctmp\u003e\nlong_upload_pack_gate=BLOCKED:UnsafeOptionError\njoined_short_upload_pack_gate=ALLOWED\nclone_result=EXPECTED_EXCEPTION:GitCommandError\nsentinel_exists=True\nsentinel_text=GITPYTHON_UNSAFE_OPTION_BYPASS\n```\n\nThe clone fails because the helper exits nonzero, but the sentinel file proves that Git executed the helper despite `allow_unsafe_options=False`.\n\nImpact:\n\nAn attacker who controls `multi_options` can bypass GitPython\u0027s default `allow_unsafe_options=False` protection and execute a local command via Git\u0027s `--upload-pack` / `-u` clone option. This is a residual bypass of an explicit GitPython security boundary, not merely a case where a caller opted into unsafe behavior.\n\nDuplicate / related advisory checks:\n\n- OSV query for `PyPI/GitPython` version `3.1.50` returned no vulnerabilities.\n- The repository\u0027s public advisories include related unsafe Git option issues, including `GHSA-x2qx-6953-8485` / `CVE-2026-42284` and `GHSA-rpm5-65cw-6hj4` / `CVE-2026-42215`. Their public affected ranges are marked as fixed before 3.1.50.\n- `GHSA-x2qx-6953-8485` describes validating `multi_options` before `shlex.split(...)`. GitPython 3.1.50 now validates after splitting, but the joined short option `-u\u003cvalue\u003e` still bypasses because the validator canonicalizes it to `u\u003cvalue\u003e` rather than `u`.\n- `GHSA-rpm5-65cw-6hj4` describes unsafe underscored kwargs such as `upload_pack=...`. The current PoC uses `multi_options=[\"-u\u003chelper\u003e\"]` against 3.1.50 and does not depend on underscored kwargs.\n- GitHub issue search for `upload-pack unsafe options` found historical related items, including CVE-2022-24439 and the earlier unsafe-options gate work, but no public issue describing this current joined-short-option residual bypass in 3.1.50.\n- GitHub issue search for `multi_options unsafe` found PR #2130, which fixed splitting of `multi_options` before checking. The current issue remains after that split because `-u\u003cvalue\u003e` is treated as option name `u\u003cvalue\u003e`, not blocked short option `u`.\n- GitHub issue searches for `u\u003cupload-pack\u003e unsafe` and `-cfoo` returned no results.\n\nSuggested remediation:\n\nWhen checking unsafe Git options, parse joined short options that take values. For clone, `-uVALUE` and `-cKEY=VALUE` should be canonicalized to `u` and `c` respectively before comparing against the unsafe option set.\n\nA safer approach is to maintain command-specific metadata for unsafe short options and recognize the bare option, split form, joined form, and long `--option=\u003cvalue\u003e` / `--option \u003cvalue\u003e` forms.",
  "id": "GHSA-v396-v7q4-x2qj",
  "modified": "2026-07-21T19:43:14Z",
  "published": "2026-07-21T19:43:14Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-v396-v7q4-x2qj"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gitpython-developers/GitPython/pull/2162"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/gitpython-developers/GitPython"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "GitPython unsafe clone option gate bypass through joined short options"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

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.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…