Skip to main content
CVE-2026-52726VulnerabilityPublished 06/10/2026

CVE-2026-52726: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') in jelmer dulwich

Affects jelmer dulwichSource CVE Database V5

0

Mentioned vulnerabilities

Not known to be exploited · CVSS 7.5 High · fixed in 1.2.5 : patch jelmer dulwich soon.

Data as of 10/25/2026, 04:40:13 UTC

CVSS 3.1 base score
7.5High
How severe the flaw is on the 0–10 CVSS scale.
Exploitation

Not in CISA KEV, no exploitation reported

EPSS
0.6% chance of exploitation in 30 days – higher than 51% of CVEsExploit Prediction Scoring System: the likelihood attackers will use this flaw soon.
Patch
Available – fixed in 1.2.5
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

What to do

Affected software

ProductAffectedFixed in
jelmerdulwichAffected
  • >=0.23.2 <1.2.5
Fixed in
  • 1.2.5
PyPIdulwichpkg:pypi/dulwichOther threatsAffected
  • >=0.23.2 <1.2.5
Fixed in
  • 1.2.5

Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.

AI-suggested

Version 1.2.5 of Dulwich patches this vulnerability. Users should upgrade to version 1.2.5 or later to remediate the issue.

Description

### Summary `dulwich.porcelain.submodule_update`, and by extension `porcelain.clone(..., recurse_submodules=True)`, materializes attacker-controlled submodule paths from a crafted upstream repository without path validation. A malicious `.gitmodules` plus a matching tree gitlink whose `path` is `.git/hooks` (or any other directory inside the parent repository's `.git` directory) 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 subsequent `git` or `dulwich` command 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 - **Package:** `dulwich` (PyPI) - **Affected versions:** `>=0.23.2, <1.2.5` - **Affected platforms:** all (Linux, macOS, Windows). Exploitation does not require a case-insensitive or NTFS filesystem, because the path written is a literal `.git/hooks` rather 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 update` CLI / `dulwich clone --recurse-submodules` CLI ### Vulnerable code The submodule path from the tree's gitlink entry (and matching `.gitmodules`) is consumed without validation in [`dulwich/porcelain/submodule.py`](https://github.com/jelmer/dulwich/blob/8efb7d19eac519cd7fac39e79ca354327897e133/dulwich/porcelain/submodule.py#L154-L234). The attacker-controlled `path` enters the loop from `iter_cached_submodules` ([`submodule.py#L154-L168`](https://github.com/jelmer/dulwich/blob/8efb7d19eac519cd7fac39e79ca354327897e133/dulwich/porcelain/submodule.py#L154-L168)): ```python for path, target_sha in submodules_to_update: path_str = ( path.decode(DEFAULT_ENCODING) if isinstance(path, bytes) else path ) submodule_name: bytes | None = None for sm_path, sm_url, sm_name in read_submodules(gitmodules_path): if sm_path == path: submodule_name = sm_name break if not submodule_name: continue ``` It flows unchecked into `os.path.join` and the filesystem ([`submodule.py#L187-L188`](https://github.com/jelmer/dulwich/blob/8efb7d19eac519cd7fac39e79ca354327897e133/dulwich/porcelain/submodule.py#L187-L188)): ```python submodule_path = os.path.join(r.path, path_str) submodule_git_dir = os.path.join(r.controldir(), "modules", path_str) ``` Finally, the attacker tree's contents are materialized into that directory via `build_index_from_tree` with no `validate_path_element` argument, defaulting to the lax validator ([`submodule.py#L229-L234`](https://github.com/jelmer/dulwich/blob/8efb7d19eac519cd7fac39e79ca354327897e133/dulwich/porcelain/submodule.py#L229-L234)): ```python build_index_from_tree( submodule_path, sub_repo.index_path(), sub_repo.object_store, tree_id, ) ``` Three issues compound: 1. `path_str` originates 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 `.gitmodules` blob via [`read_submodules`](https://github.com/jelmer/dulwich/blob/8efb7d19eac519cd7fac39e79ca354327897e133/dulwich/config.py#L1637-L1665), which also performs no validation. 2. `submodule_path = os.path.join(r.path, path_str)` therefore resolves to an attacker-chosen directory anywhere on disk (e.g. `<worktree>/.git/hooks`). 3. [`build_index_from_tree`](https://github.com/jelmer/dulwich/blob/8efb7d19eac519cd7fac39e79ca354327897e133/dulwich/index.py#L2034-L2044) is called without `validate_path_element`, so it defaults to `validate_path_element_default`, which only rejects literal `.git`, `.`, and `..`. It does not refuse a `root_path` that is itself inside the parent's `.git` directory, 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)` at [`dulwich/porcelain/__init__.py:1548-1551`](https://github.com/jelmer/dulwich/blob/8efb7d19eac519cd7fac39e79ca354327897e133/dulwich/porcelain/__init__.py#L1548-L1551) calls `submodule_update(repo, init=True, recursive=True)` once the parent clone completes, reaching the unsanitized loop at [`submodule.py#L154-L234`](https://github.com/jelmer/dulwich/blob/8efb7d19eac519cd7fac39e79ca354327897e133/dulwich/porcelain/submodule.py#L154-L234). The CLI command `dulwich clone --recurse-submodules <url>` reaches the same sink via [`dulwich

Impact

An attacker who controls or can influence the upstream repository can craft submodules that cause malicious executables to be written into the victim's .git/hooks directory. These executables run with the privileges of the user executing Git or Dulwich commands that trigger the hooks, resulting in arbitrary code execution. This can lead to full compromise of the user's environment where Dulwich is used.

Summary

CVE-2026-52726 is a path traversal vulnerability (CWE-22) in the Dulwich library's submodule update mechanism. Specifically, dulwich.porcelain.submodule_update and porcelain.clone with recurse_submodules=True do not validate submodule paths from a malicious .gitmodules file and corresponding gitlink tree entries. This flaw enables an attacker to place executable files into sensitive directories inside the parent repository's .git directory, such as .git/hooks, preserving executable permissions. When Git or Dulwich subsequently runs hooks from these directories, the attacker's code executes. This vulnerability parallels fixes made in upstream Git for CVE-2024-32002 and CVE-2024-32004 but was not applied to Dulwich until version 1.2.5, which patches the issue.

Weaknesses

References

Community reviews

Loading reviews…