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.gitor 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.5for the Windows checkout issue (CVE-2026-42305)dulwich>= 0.24.0, < 1.2.5for theformat_patchtraversal issue (CVE-2026-47712)
Patched release:
dulwich1.2.5
Affected usage patterns include:
porcelain.clone- repository fetch or checkout flows built on Dulwich
- the
dulwichCLI 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, andLPT1
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:
- directory traversal via separators or
.. - Windows-hostile characters such as
: - 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 documentedcore.protectNTFSsetting was being read incorrectly. - For
CVE-2026-47712, you can reduce risk by usingstdout=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.