Six Git Vulnerabilities in One Release: Hardening Code That Clones Untrusted Repositories

The Git project's November 10, 2026 security release fixes six flaws, including code execution during recursive clones. What each one means for CI, build bots and developer tooling, plus an illustrative Python guard for version checks and safer clones.

On November 10, 2026 the Git project released Git 2.56.1 to address six security vulnerabilities. According to the GitHub Blog announcement by Elijah Newren, they include “arbitrary code execution when recursively cloning a malicious repository, writes to unintended files, and memory corruption when processing merges or binary patches.” Backports shipped for 2.55.1, 2.54.1, 2.53.1, 2.52.1, 2.51.3 and 2.50.2.

If your code runs git against repositories you did not write (CI runners, dependency scanners, code-review bots, IDE plugins), this release matters even if your own laptop updates automatically.

The six fixes at a glance

The descriptions below come from the GitHub Blog post, except the CVSS line, which is from the Git project’s advisory GHSA-vp2w-7xxj-jvxr. As of 2026-11-11, NVD has no record for five of the six CVEs (CVE-2026-105437 is listed as “Received”, with only a GitLab CNA score).

  • CVE-2026-111760, code execution during recursive clones. An integer truncation in index handling lets an extremely long path pass validation under one name and be written under a shorter one. An attacker can use that to replace a submodule’s .git file with a symbolic link to a directory holding malicious configuration or hooks, and Git can then run attacker-controlled code during a recursive clone. The GitHub Security Advisory GHSA-vp2w-7xxj-jvxr rates it High, CVSS 8.0 (AV:N/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:H).
  • CVE-2026-113408, path traversal via the server’s advertised HEAD. When cloning an empty repository over protocol v2, a server can advertise a HEAD symref to an unborn branch. Git stored the target without validation, so with the files reference backend a later branch update can write outside the intended reference directory.
  • CVE-2026-114467, stack corruption when stripping signatures. Git assumed at most two GPG signatures. A message with more can write past a fixed-size stack array, for example when merging a crafted tag.
  • CVE-2026-114469, out-of-bounds reads in binary patches. The roughly 1 GiB patch size check looked only at the compressed input, so a small patch could expand past the limit and crash Git.
  • CVE-2026-105437, heap corruption in large merges. A size calculation can overflow, including with many conflicts combined with a large conflict-marker-size attribute.
  • CVE-2026-114466, filesystem access via fast-import. rewrite-submodules-from, rewrite-submodules-to and export-pack-edges were not blocked in untrusted streams. They are now rejected unless the caller passes --allow-unsafe-features.

Why the clone bug is a pipeline problem

The classic pattern is a service that takes a repository URL from a user or a pull request and runs git clone --recurse-submodules. The attacker controls the repository, so the attacker controls the bytes Git parses. GitHub’s guidance is direct: “If you cannot update immediately, clone only from trusted servers and avoid recursive clones of untrusted repositories,” and avoid merging untrusted content or processing binary patches or fast-import streams from untrusted sources. It adds that these precautions “do not replace installing the fixes.”

The advisory text itself says Git can be fooled into overwriting a gitfile with a symbolic link, and that the authors “currently believe this can only be triggered when the --recurse-submodules option is used.” Treat that as a belief of the maintainers at publication, not a guarantee.

Illustrative guard in Python

The following sketch is illustrative, not production-ready. It refuses to run an unpatched Git series and clones without submodules. Adapt the policy to your environment.

import re
import subprocess
from urllib.parse import urlparse

# Minimum patched release per series, from the November 10, 2026 announcement.
PATCHED = {
    (2, 56): 1, (2, 55): 1, (2, 54): 1, (2, 53): 1,
    (2, 52): 1, (2, 51): 3, (2, 50): 2,
}


GIT_ENV = {"GIT_TERMINAL_PROMPT": "0", "PATH": "/usr/bin:/bin"}


def git_version() -> tuple[int, int, int]:
    # Use the same environment as the clone so we check the binary that will run.
    out = subprocess.run(
        ["git", "--version"], capture_output=True, text=True, check=True, env=GIT_ENV
    ).stdout
    m = re.search(r"git version (\d+)\.(\d+)\.(\d+)", out)
    if not m:
        raise RuntimeError(f"unrecognised git version string: {out!r}")
    return int(m[1]), int(m[2]), int(m[3])


def is_patched(v: tuple[int, int, int]) -> bool:
    major, minor, patch = v
    if (major, minor) in PATCHED:
        return patch >= PATCHED[(major, minor)]
    # Newer series are assumed patched; older series have no listed fix.
    return (major, minor) > (2, 56)


def safe_clone(url: str, dest: str) -> None:
    if urlparse(url).scheme != "https":
        raise ValueError("only https URLs are allowed")
    if not is_patched(git_version()):
        raise RuntimeError("git is not on a patched release; update first")
    subprocess.run(
        ["git", "clone", "--no-recurse-submodules", "--depth", "1", "--", url, dest],
        check=True,
        timeout=300,
        env=GIT_ENV,
    )

What this does and does not do: the version gate depends on the patched-release list above (our assessment: a series older than 2.50 has no listed fix, so the guard rejects it). Distribution or vendor builds with backported fixes (for example Debian or Apple Git) report older version numbers and will be rejected by this check, so adapt it to your package manager’s advisories. Skipping submodules should avoid the exposure to CVE-2026-111760, on the maintainers’ current belief, but it does nothing for the protocol v2 HEAD issue, which is only addressed by the patched client. The restricted PATH and environment are assumptions for a Linux runner; adjust them if your Git lives elsewhere or needs a proxy.

Checklist

  1. Update to 2.56.1 or the patched release of your series, and update anything that bundles its own Git (GitHub says Desktop, the Copilot app, Codespaces and Actions will receive patched versions).
  2. Inventory automation that runs git merge, git apply or git fast-import on external input, and keep --allow-unsafe-features off unless you trust the stream producer.
  3. Run clones of untrusted repositories in a disposable, low-privilege sandbox with no secrets mounted. This is our assessment, not a vendor requirement.
  4. GitHub Enterprise Server administrators should move to 3.19.15, 3.20.11, 3.21.9 or 3.22.4.

Unverified at time of writing

The advisory page for CVE-2026-111760, when we opened it on 2026-11-11, still listed “Affected versions: all” and “Patched versions: none” and said patches were being discussed on the git-security mailing list, which conflicts with the blog post announcing fixed releases. The Git project’s 2.56.1 release notes list the same six CVEs and the same backport series, so the version numbers do not rest on the blog alone; the stale text is the advisory page, not the releases. The other five CVEs have GitLab work items, which we did not open. The mechanism descriptions rest on the GitHub Blog summary. We did not run the Python guard, and we found no evidence of in-the-wild exploitation of any of the six as of 2026-11-11 in the GitHub Blog post, the advisory, the CISA KEV catalog (none of the six is listed) or NVD.

References