Repository navigation
Update dependency dulwich to v1.2.9 [SECURITY] - #112
Open
renovate[bot] wants to merge 1 commit into
Open
renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
renovate
Bot
force-pushed
the
renovate/pypi-dulwich-vulnerability
branch
from
November 11, 2026 03:34
3d1d6fe to
7b44819
Compare
renovate
Bot
force-pushed
the
renovate/pypi-dulwich-vulnerability
branch
from
November 12, 2026 15:44
7b44819 to
9e03ed8
Compare
renovate
Bot
force-pushed
the
renovate/pypi-dulwich-vulnerability
branch
from
November 12, 2026 22:59
9e03ed8 to
09783de
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
This PR body was truncated due to platform limits.
This PR contains the following updates:
1.1.0→1.2.9Dulwich has an arbitrary file write via NTFS-hostile tree entries on Windows
CVE-2026-42305 / GHSA-897w-fcg9-f6xj
More information
Details
Impact
Arbitrary file write leading to remote code execution when cloning or checking out a malicious Git repository on Windows.
Dulwich's path-element validator accepted tree entries whose filenames contained bytes that Windows interprets as structural path syntax:
executes that hook on the next git commit, giving the attacker arbitrary code execution in the victim's user context. The same primitive can be used with ..\outside.txt to escape the work tree.
1 was rejected; git2, git10, GIT1, etc. were all accepted.Contributing configuration bugs made matters worse. The core.protectNTFS and core.protectHFS settings were looked up under a wrong option name and so user-set values were silently ignored, and core.protectNTFS only defaulted to true on Windows (Git upstream has defaulted it to true everywhere since CVE-2019-1353). Both have been corrected.
Anyone who clones, fetches, or checks out an untrusted repository with Dulwich on Windows - either through the Dulwich CLI, porcelain.clone, or any downstream tool built on Dulwich - is impacted. POSIX clones are not directly exploitable (on POSIX \ is a literal filename byte), but a POSIX user can unknowingly propagate a malicious tree to Windows consumers via push or re-publication.
Patches
Fixed in Dulwich 1.2.5. Users should upgrade to 1.2.5 or later.
The fix lives in three commits:
Workarounds
There is no effective pre-patch workaround. On affected versions the core.protectNTFS configuration key was silently ignored, so setting it to true does not mitigate the issue. Users who cannot upgrade should avoid cloning, fetching, or checking out untrusted repositories with Dulwich on Windows. After upgrading the NTFS validator is on by default on every platform, so no additional configuration is required.
Resources
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Dulwich Vulnerable to Command Injection via Merge Driver Path
CVE-2026-42563 / GHSA-9277-mp7x-85jf
More information
Details
Summary
Dulwich's
ProcessMergeDriversubstitutes the file path (from the git tree, controllable by an attacker via a malicious branch) into the merge driver command via the%Pplaceholder and executes it withsubprocess.run(..., shell=True). An attacker who can cause a victim to merge an untrusted branch can achieve arbitrary command execution by crafting malicious file paths.Description
merge.pyline 195 — path from merge tree (from repository content when merging untrusted branch)merge_drivers.pylines 124–127 —subprocess.run(cmd, shell=True)wherecmdincludes path via%Pplaceholder%Pplaceholder.Resources
dulwich/merge_drivers.py(lines 119–129)Proof of Concept
Fix
merge_drivers_shell_escape.patch
Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Dulwich doesn't sanitize commit subjects in
porcelain.format_patchCVE-2026-47712 / GHSA-555p-6grf-mh7f
More information
Details
Impact
dulwich.porcelain.format_patch(outdir=...) derives each patch filename from the commit's subject line. Prior to this fix, get_summary only replaced spaces with dashes - path separators (/, ), parent-directory components (..), and other filename-hostile characters (e.g. :) were preserved verbatim and passed straight into os.path.join(outdir, f"{i:04d}-{summary}.patch").
A malicious commit subject could therefore direct the generated patch file outside the requested outdir. Reduced examples:
two directories above outdir.
Related issues from the same root cause:
Anyone calling porcelain.format_patch (or the dulwich format-patch CLI) against untrusted commits - for example, a service that runs format-patch over user-supplied repositories or pull requests - could have patch files written to attacker-chosen locations within the process's write permissions.
Patches
Fixed in Dulwich 1.2.5. Users should upgrade.
dulwich.patch.get_summary now mirrors git's format_sanitized_subject: only
[A-Za-z0-9._]are kept, runs of other characters collapse to a single -, consecutive . collapse to a single ., trailing ./- are stripped, and the result is length-limited. This makes the returned string safe to embed as a filename component, so format_patch can no longer be steered out of outdir via the commit subject.Workarounds
Until upgrading, callers that pass untrusted commits to porcelain.format_patch can:
Resources
Severity
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Dulwich has unbounded memory allocation in receive-pack from crafted thin packs
CVE-2026-47734 / GHSA-xrvj-v92f-53gj
More information
Details
Impact
An uncontrolled-resource-consumption (memory exhaustion) denial-of-service vulnerability (CWE-400 / CWE-789).
A client with push access could push a tiny crafted thin pack (~174 bytes) whose delta header declares a huge dest_size. When dulwich ingested it via add_thin_pack / apply_delta, it would allocate hundreds of MB of memory based on that attacker-controlled size, with no relationship to the actual bytes received.
Who is impacted: Operators running a Dulwich-based Git server that exposes git-receive-pack (i.e. accepts pushes) -
for example via dulwich.server functionality, the HTTP smart server, or anything built on ReceivePackHandler.
Patches
Patched in 1.2.5.
add_thin_pack now accepts a max_input_size keyword (bytes; 0/None = unlimited, matching git's semantics), and ReceivePackHandler reads receive.maxInputSize from the repository config and passes it through. Wire reads are counted and a PackInputTooLarge exception is raised once the cap is exceeded - equivalent to git index-pack --max-input-size.
Users should upgrade to Dulwich 1.2.5 or later and set receive.maxInputSize in their server's repository config to a sane bound for their environment.
Workarounds
On unpatched versions, receive.maxInputSize has no effect, so it cannot be used as a workaround. Until upgrading, operators should:
Resources
Severity
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Dulwich's submodule path traversal in porcelain.submodule_update / porcelain.clone(recurse_submodules=True) yields RCE via attacker-dropped .git/hooks payload
CVE-2026-52726 / GHSA-gfhv-vqv2-4544
More information
Details
Summary
dulwich.porcelain.submodule_update, and by extensionporcelain.clone(..., recurse_submodules=True), materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious.gitmodulesplus a matching tree gitlink whosepathis.git/hooks(or any other directory inside the parent repository's.gitdirectory) causes the attacker's submodule tree contents to be written directly into the victim's.git/hooks/directory, preserving executable mode bits. The dropped executables are then run by any subsequentgitordulwichcommand that invokes the matching hook, resulting in arbitrary code execution.This is the dulwich equivalent of the upstream Git fixes for CVE-2024-32002 / CVE-2024-32004, which were never propagated into dulwich's separately implemented submodule porcelain.
Affected
dulwich(PyPI)>=0.23.2, <1.2.5.git/hooksrather than a case- or short-name-aliased form.Affected entry points:
dulwich.porcelain.submodule_update(repo, init=True, recursive=True)dulwich.porcelain.clone(source, target, recurse_submodules=True)dulwich submodule updateCLI /dulwich clone --recurse-submodulesCLIVulnerable code
The submodule path from the tree's gitlink entry (and matching
.gitmodules) is consumed without validation indulwich/porcelain/submodule.py.The attacker-controlled
pathenters the loop fromiter_cached_submodules(submodule.py#L154-L168):It flows unchecked into
os.path.joinand the filesystem (submodule.py#L187-L188):Finally, the attacker tree's contents are materialized into that directory via
build_index_from_treewith novalidate_path_elementargument, defaulting to the lax validator (submodule.py#L229-L234):Three issues compound:
path_stroriginates from the parent repository's tree gitlink entry (attacker-controlled) and is never validated against.git,.., or other path-traversal patterns. The same value is read from the attacker-supplied.gitmodulesblob viaread_submodules, which also performs no validation.submodule_path = os.path.join(r.path, path_str)therefore resolves to an attacker-chosen directory anywhere on disk (e.g.<worktree>/.git/hooks).build_index_from_treeis called withoutvalidate_path_element, so it defaults tovalidate_path_element_default, which only rejects literal.git,., and... It does not refuse aroot_paththat is itself inside the parent's.gitdirectory, and it honors the attacker tree's file modes including executable bits (0o100755).Reachability
A direct production call path from a user invocation:
porcelain.clone(source, target, recurse_submodules=True)atdulwich/porcelain/__init__.py:1548-1551callssubmodule_update(repo, init=True, recursive=True)once the parent clone completes, reaching the unsanitized loop atsubmodule.py#L154-L234.The CLI command
dulwich clone --recurse-submodules <url>reaches the same sink viadulwich/cli.py:2131.Any service that exposes
porcelain.clone(..., recurse_submodules=True)on attacker-supplied URLs is exposed: CI runners, repository import tools, package resolvers that use dulwich as a pure-Python git, and language-server "fetch dependency from git" features.Proof of concept
End-to-end against pip-installed
dulwich==1.2.4, demonstrating both the path-traversal primitive and the resulting code execution when the victim subsequently runsgit. The payload writes a marker file rather than performing any destructive action.The trigger surface is broader than this proof of concept: the dropped file fires for any matching hook name (
post-checkout,pre-commit,post-merge,post-rewrite,post-applypatch, and others). dulwich itself executes several hooks (pre-commit,commit-msg,post-commit,pre-receive,update,post-receive; seedulwich/hooks.pyanddulwich/repo.py), so a victim using only dulwich is also reachable without upstream Git.Credit
tonghuaroot
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Dulwich: Symlink directory traversal in filter-branch index_filter via cross-commit state persistence
GHSA-5fqc-mrg8-w798
More information
Details
Summary
Dulwich's
filter_branch.pyCommitFilter._apply_index_filter()is vulnerable to symlink directory traversal. When processing commit history, materialized tree entries (including symlinks) persist in the working directory between commits, allowing a symlink from an ancestor commit to redirect file writes from a descendant commit to arbitrary filesystem locations.Root Cause
_apply_index_filter()atdulwich/filter_branch.py:212callsbuild_index_from_tree(".", tmp_index_path, ...)which materializes all tree entries to the current working directory. Thefinallyblock (line 229-230) only cleans up the temporary index file (os.unlink(tmp_index_path)) — NOT the filesystem files written to CWD. Whenprocess_commit()processes parents recursively first (line 260), files materialized from ancestor commits persist and affect processing of descendant commits.On dulwich 1.2.7,
build_file_from_blob()has no symlink protection, andvalidate_path_elementonly validates name patterns, not filesystem state.Impact
An attacker can craft a malicious repository where running
filter_branchwith an index filter writes attacker-controlled content to arbitrary filesystem locations via symlink traversal. This achieves RCE if the write targets.git/hooks/.Attack Scenario
evil(mode 120000, symlink →/target_dir)evil/payload(mode 100644, attacker content)filter_branchwith an index filterprocess_commit()processes ancestor first → materializesevilas symlink to/target_dirin CWDos.path.exists("./evil")→ True (symlink exists).build_file_from_blob(blob, mode, "./evil/payload")→open("./evil/payload", "wb")follows intermediate symlink → writes to/target_dir/payloadSuggested Fix
Clean the CWD between commit iterations in
_apply_index_filter(), or verify that no intermediate path components are symlinks before writing files.Reported by zx (Jace)
Severity
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Dulwich: Symlink write-through in checkout(paths=[]) via raw os.open bypasses all symlink protections
GHSA-8w8g-wq8h-fq33
More information
Details
Summary
Dulwich's
porcelain.checkout(paths=[...])code path writes files using rawos.open(file_path, O_WRONLY|O_CREAT|O_TRUNC, mode)followed byf.write(obj.data). This code path does NOT callbuild_file_from_blob()at all, completely bypassing any symlink protections (including the unreleased d09f8af fix).os.openwithoutO_NOFOLLOWfollows symlinks at both the target file and intermediate directories, allowing arbitrary file writes.Root Cause
At
dulwich/porcelain/__init__.py:5661-5675, thecheckout(paths=[...])implementation:_checked_worktree_path()(line 601-631) only performs name validation — checking that the path doesn't start with/or\\and that components passINVALID_DOTNAMESchecks. It performs zero filesystem symlink detection.Impact
An attacker can craft a malicious repository that, when a victim clones it and runs
checkout(paths=[...]), writes attacker-controlled content (with attacker-controlled permissions) to any filesystem location accessible to the user. Writing to.git/hooks/post-checkoutachieves RCE on the next git checkout.Attack Scenario
triggeras a symlink (mode 120000, content../../.git/hooks/post-checkout), and tagv1.0hastriggeras an executable file (mode 100755, content#!/bin/sh\nmalicious_payload)trigger→../../.git/hooks/post-checkout(a symlink)porcelain.checkout(repo, target="v1.0", paths=["trigger"])to restore a specific file from a tag_checked_worktree_path(r, "trigger")passes — name validation only, no symlink checkos.open("trigger", O_WRONLY|O_CREAT|O_TRUNC, 0o755)follows the symlink → opens.git/hooks/post-checkoutfor writingf.write(obj.data)writes the malicious payload to the hookSuggested Fix
Replace the raw
os.openpath with a call tobuild_file_from_blob(once that function is hardened against intermediate symlinks), or add explicit symlink detection: resolve the path withos.path.realpath()and verify it stays within the worktree root before opening.Reported by zx (Jace)
Severity
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Dulwich: Symlink directory traversal in stash pop allows arbitrary file write via intermediate directory symlinks
GHSA-cm62-gvxx-vmxx
More information
Details
Summary
Dulwich's
stash.py:pop()function is vulnerable to symlink directory traversal, allowing an attacker to write arbitrary files outside the repository worktree when a victim pops a stash in a malicious repository.Root Cause
The
pop()function atdulwich/stash.py:236usesos.path.exists(parent_dir)to check if a parent directory exists before writing stashed files.os.path.exists()follows symlinks, so when an intermediate directory in the path is a symlink pointing outside the worktree (e.g.,link → ../../.git/hooks), the check passes and subsequent file writes resolve through the symlink.The
validate_path()function (line 228) only validates path component names againstINVALID_DOTNAMES— it performs zero filesystem symlink detection. On dulwich 1.2.7 (latest release),build_file_from_blob()has no symlink protection whatsoever.Impact
An attacker can craft a malicious repository that, when a victim clones it and performs a stash pop operation, writes attacker-controlled content to arbitrary filesystem locations. Writing to
.git/hooks/post-checkoutachieves Remote Code Execution on the victim's machine on the next git checkout operation.Attack Scenario
maincontaininglink(symlink →../../.git/hooks) and branchfeaturecontaininglink/post-checkout(executable payload)main— symlinklinkexists in worktree)feature, makes changes, runsstash.push()main(restoring thelinksymlink)stash.pop(0)— stash containslink/post-checkoutos.path.exists("link")returns True (symlink to existing directory),os.makedirsskippedbuild_file_from_blob(blob, mode, "link/post-checkout")→open("link/post-checkout", "wb")follows the intermediate symlink → payload written to.git/hooks/post-checkoutSuggested Fix
Before writing any file, verify that no component of the target path resolves through a symlink outside the worktree. Use
os.path.realpath(parent_dir)and confirm it stays within the repository root. Alternatively, useos.open()withO_NOFOLLOWon each path component.Reported by zx (Jace)
Severity
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Dulwich: Infinite Loop Denial of Service (DoS) in Packfile Object Resolution
GHSA-35mr-4567-66vg
More information
Details
Affected file
dulwich/pack.py(Method:Pack.resolve_object)Description / Summary
A High-severity Denial of Service (DoS) vulnerability exists in the
Pack.resolve_objectmethod. When resolving anOFS_DELTAobject, the resolver calculates the base offset usingbase_offset = obj_offset - delta_offset.If a malicious packfile contains an
OFS_DELTAobject wheredelta_offsetis0, the calculationobj_offset - 0resolves back to the current object's own offset. Because the implementation lacks a depth counter, a "visited" set, or an explicit rejection ofdelta_offset == 0, the resolver enters an infinite recursive loop, exhausting CPU resources and eventually crashing the process.Vulnerable Code Breakdown (
dulwich/pack.py):Potential impact
An attacker can trigger this infinite loop via any operation that walks packfiles (e.g.,
dulwich clone,fetch,cat-file, or internalPack.__getitem__lookups).dulwich(web interfaces, CI/CD runners) will hang or crash, preventing legitimate repository access.gitC client explicitly guards against this:if (!base_offset) die("delta offset == 0 is invalid");.POC (Proof of Concept)
The following Python script generates a 44-byte packfile that triggers the loop:
Possible solution
Pack.resolve_objectto rejectdelta_offset == 0:MAX_DELTA_DEPTH = 50) to prevent long, non-looping chains of deltas (OFS or REF).Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Dulwich: Arbitrary File Write (RCE) on Windows via Unvalidated Drive Letters in Tree Paths
GHSA-8mcx-5rqc-vhmf
More information
Details
Affected files
dulwich/index.py(Methods:validate_path_element_ntfs,_tree_to_fs_path)dulwich/porcelain/__init__.py(Method:_checked_worktree_path)Description / Summary
A High-severity Path Traversal vulnerability exists in Dulwich's checkout logic when running on Windows. The functions responsible for validating NTFS paths strictly reject
.gitvariants, Alternate Data Streams (ADS),git~1short names, and reserved device names, but they completely fail to check for DOS drive letter prefixes.A malicious Git tree can contain an entry named
C:. When Dulwich processes this tree on a Windows client, the string passes thevalidate_path_element_ntfscheck. Later,_tree_to_fs_pathpasses this path toos.path.join(root, b"C:\\\\Users\\\\...").On Windows, if the second argument to
os.path.joincontains an absolute drive letter, therootpath is completely discarded. As a result, Dulwich writes the repository file to the absolute path outside of the intended Git worktree.While the standard C
gitclient explicitly blocks this viahas_dos_drive_prefix()inpath.c, Dulwich lacks this protection. Because Git trees are cross-platform, an attacker can author a malicious repository on Linux and wait for a Windows victim (or CI runner) to clone it.Potential impact
This vulnerability allows an attacker to achieve Arbitrary File Write, which can trivially be escalated to Remote Code Execution (RCE) or total system compromise on the victim's Windows machine.
Attack vectors include:
C:\\Users\\<victim>\\.gitconfigfile to setcore.sshCommandto an arbitrary executable, granting RCE the next time the user interacts with Git.C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\StartUp\\.C:\\Users\\<victim>\\.ssh\\authorized_keysto compromise remote servers accessible by the victim.Proof of Concept (PoC)
The following Python script (runnable on Linux) generates a malicious Git repository containing a payload that targets Windows clients.
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
dulwich/dulwich (dulwich)
v1.2.9Compare Source
Highlights of this release:
Security fixes
C:), whichos.path.joinwould otherwise treat as absolute and let a crafted tree write outside the work tree. (reported by Luke Baton)read_zlib_chunksnow caps the inflated size at the declareddecomp_len, and loose-object parsing acceptsmax_sizedefaulting to git'score.bigFileThreshold(512 MiB). (reported by Luke Bento)OFS_DELTApack entries whosedelta_base_offsetis zero. Such an entry references itself, soPack.resolve_objectwould loop forever growing its delta stack until OOM. (reported by Luke Banto)Transfer-Encoding: chunkedrequest bodies indulwich.web: strip chunk extensions, reject negative sizes, enforce per-chunk and per-request size limits, and cap requests that omitContent-Length..bitmapindex raisesValueErrorinstead of hanging in an unbounded loop that exhausts memory.Other changes
git-upload-pack/git-receive-packPOSTs whoseContent-Typeis not the exactapplication/x-<service>-requestvalue, matchinggit http-backend.dulwich.web: Warn at startup that runningpython3 -m dulwich.webis not intended as a production web server (no authentication, etc.).See NEWS for the full list.
v1.2.8Compare Source
Security
build_file_from_blobin path-restrictedporcelain.checkout(paths=...)instead of a rawos.open. The old path followed a symlink left at the target, so a crafted repository could write attacker content outside the work tree (e.g. into.git/hooks) oncheckout.filter-branch --index-filterin memory instead of materializing tree entries into CWD. Persisted entries between commits let a symlink from an ancestor commit redirect a descendant's writes outside the work tree.stash.pop. The oldos.path.existscheck followed symlinks, so a crafted stash whose parent path was a symlink already present in the worktree (e.g.link->.git/hooks) let the subsequent write land outside the work tree.porcelain.checkout,restoreandreset_filewrites whose leading directory is an existing symlink, so a crafted repository can no longer landsubas a link to.git/hooksand have a write tosub/anythingtraverse it.0o644/0o755beforechmodon checkout and patch apply, matching git. An untrusted tree entry or patch `new file mode` could otherwise set setuid/setgid/sticky or world-writable bits on a materialized file.int(sizestr, 16)accepted a leading-, whose negative length made the following `read(size - 4)` slurp the rest of the stream and caused `PktLineParser` to loop without consuming its buffer. (#2267)TCPGitClientbefore invoking thecore.gitProxycommand, matching theSubprocessSSHVendorguard, so a URL like `git://-oProxyCommand=...` can no longer be interpreted as a flag by the proxy program. (#2240)Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.