high

CVE

CVE-2026-42305, CVE-2026-47712

CWE

CWE-22

Affected Surface

dulwich >= 0.10.0 and < 1.2.5 when cloning, fetching, or checking out untrusted repositories on Windows, dulwich >= 0.24.0 and < 1.2.5 when porcelain.format_patch or dulwich format-patch processes untrusted commits, Downstream Python tools that embed Dulwich for Git operations, CI services and desktop tooling that materialize attacker-controlled repositories or patch files

Dulwich 1.2.5 is a meaningful security release for any Python application or internal tool that uses Dulwich as its Git engine. The new release fixes two distinct pathname-handling flaws:

  • CVE-2026-42305: Windows clone, fetch, and checkout paths could materialize attacker-controlled tree entries as files inside .git or outside the work tree.
  • CVE-2026-47712: porcelain.format_patch(outdir=...) could derive patch filenames from unsanitized commit subjects and write outside the requested output directory.

Taken together, these bugs are a reminder that Git libraries are not just parser code. They are filesystem mediators. When a Git tree name or commit subject is allowed to cross the API boundary without strict normalization, an attacker can turn repository metadata into file writes.

Affected package

Affected PyPI package:

  • dulwich >= 0.10.0, < 1.2.5 for the Windows checkout issue (CVE-2026-42305)
  • dulwich >= 0.24.0, < 1.2.5 for the format_patch traversal issue (CVE-2026-47712)

Patched release:

  • dulwich 1.2.5

Affected usage patterns include:

  • porcelain.clone
  • repository fetch or checkout flows built on Dulwich
  • the dulwich CLI on Windows
  • porcelain.format_patch
  • any service that formats patches from untrusted repositories, pull requests, or imported commit objects

CVE-2026-42305: Windows path handling let tree entries become writes

The higher-severity issue is CVE-2026-42305. Upstream describes it as arbitrary file write leading to remote code execution when cloning or checking out a malicious repository on Windows.

The root cause is that Dulwich accepted tree entry names containing bytes that are harmless as literal filenames on POSIX but structural on Windows:

.git\hooks\pre-commit.exe
..\outside.txt
.git::$INDEX_ALLOCATION
git~2

Each of those names maps to a different Windows-specific danger:

  • \ becomes a real path separator on Windows, so a single tree entry can materialize nested directories or files.
  • : invokes NTFS alternate data stream behavior.
  • git~<digits> can resolve to the short-name alias of .git.

That turns an attacker-controlled repository into a write primitive. The advisory’s most direct example is the most important one:

.git\hooks\pre-commit.exe

On POSIX this is just a filename containing a backslash byte. On Windows it becomes a file planted under .git/hooks/. Git for Windows will execute that hook on the next git commit, which is why the advisory treats this as a route to code execution in the victim user’s context.

The same class also supports work-tree escape:

..\outside.txt

If the checkout code treats the entry as a normal path element, Windows will not.

The configuration guardrail was misread

The bug was made worse by a configuration mistake. Pre-fix code used the wrong option name when reading the NTFS protection toggle:

if config.get_boolean(b"core", b"core.protectNTFS", os.name == "nt"):
    validate_path_element = validate_path_element_ntfs

That is not the documented key. The fixed code switches to the proper option name and changes the default to True on every platform:

if config.get_boolean(b"core", b"protectNTFS", True):
    validate_path_element = validate_path_element_ntfs

That change matters even on POSIX. A repository created or mirrored on Linux can still be cloned later on Windows, so rejecting NTFS-hostile names only on Windows is not enough.

What 1.2.5 changes for path validation

The 1.2.5 patch also hardens the actual validator. Upstream commit metadata shows the new checks added to validate_path_element_ntfs:

if b"\\" in element:
    return False
if b":" in element:
    return False

if _is_ntfs_dotgit_short_name(normalized):
    return False
if _is_reserved_windows_device_name(normalized):
    return False

Those checks close several classes at once:

  • backslash-based path splitting
  • alternate data stream abuse such as .git::$INDEX_ALLOCATION
  • git~<digits> short-name aliases of .git
  • reserved device names such as NUL, AUX, COM1, and LPT1

This is a surgical fix with a clear goal: treat repository tree names as hostile filesystem input, not as opaque bytes.

CVE-2026-47712: format_patch trusted the commit subject as a path component

The second issue is lower severity but still relevant to automation that handles untrusted commits. Before 1.2.5, dulwich.patch.get_summary() only replaced spaces with dashes:

def get_summary(commit):
    decoded = commit.message.decode(errors="replace")
    lines = decoded.splitlines()
    return lines[0].replace(" ", "-") if lines else ""

That summary was then embedded into:

os.path.join(outdir, f"{i:04d}-{summary}.patch")

Because the code did not strip /, \, .., or :, a malicious commit subject could steer the patch write outside outdir.

Reduced examples from the advisory:

x/../../x     -> <outdir>/0001-x/../../x.patch
x\..\..\x     -> Windows escape path
a:b           -> invalid or special filename behavior on Windows

This matters anywhere a service or local automation runs format_patch over untrusted repositories, such as patch exporters, code review bridges, import pipelines, or repo-analysis jobs that persist patch artifacts to disk.

How the format_patch fix works

Upstream commit c2446e51b adds a dedicated sanitizer that mirrors Git’s own format_sanitized_subject behavior:

def _sanitize_subject_for_filename(text: str, max_length: int = 52) -> str:
    # keep only [A-Za-z0-9._]
    # collapse other runs to "-"
    # collapse repeated "."
    return "".join(result)[:max_length].rstrip(".-")

get_summary() now calls that sanitizer instead of doing a single replace(" ", "-").

This fixes three operational problems at once:

  1. directory traversal via separators or ..
  2. Windows-hostile characters such as :
  3. pathologically long filenames that exceed normal filesystem expectations

The important design lesson is the same one that shows up in archive extractors and package managers: “safe enough for display” is not the same thing as “safe to embed in a filesystem path.”

Detection and scoping

Inventory the installed version first:

python3 -c "import dulwich; print(dulwich.__version__)"
pip show dulwich

Then locate the code paths that matter in your environment:

rg -n "porcelain\\.clone|porcelain\\.format_patch|format-patch" .

Treat these cases as highest priority:

  • Windows developer tools or CI jobs that clone or check out untrusted repositories with Dulwich
  • services that accept repository URLs or Git objects from users and materialize them with Dulwich
  • automation that writes patch files to disk from attacker-controlled or third-party commits

For CVE-2026-42305, review whether any Windows hosts processed untrusted repositories while running affected versions. For CVE-2026-47712, review any service that calls porcelain.format_patch with user-controlled commit history.

Remediation

Upgrade to the fixed release:

pip install --upgrade "dulwich>=1.2.5"

If you package dependencies via lockfiles or vendored artifacts, make sure the fixed version is what the runtime actually resolves.

Temporary guidance differs by issue:

  • For CVE-2026-42305, upstream says there is no effective pre-patch workaround because the documented core.protectNTFS setting was being read incorrectly.
  • For CVE-2026-47712, you can reduce risk by using stdout=True, choosing the destination path yourself, and rejecting resolved paths that escape the intended output directory.

If you cannot patch immediately, the safest short-term control is to avoid using vulnerable Dulwich builds on untrusted repositories, especially on Windows.

Why this release deserves attention

Many teams treat Git libraries as plumbing, but Dulwich sits directly on a trust boundary between attacker-controlled repository data and local filesystem writes. 1.2.5 fixes two separate cases where metadata crossed that boundary with insufficient normalization.

For AppSec teams, the takeaway is straightforward: if your Python tooling clones repositories, checks out work trees, or generates patch files from untrusted inputs, Dulwich is not just a dependency to update eventually. It is a dependency whose filesystem mediation rules need to be correct right now.

References