Dulwich doesn't sanitize commit subjects in `porcelain.format_patch`

Description

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:

  • x/../../x produced <outdir>/0001-x/../../x.patch, resolving
    two directories above outdir.
  • x....\x produced the equivalent escape on Windows, here \ is also a path separator.

Related issues from the same root cause:

  • Subjects containing characters that are illegal in Windows filenames (e.g. :) caused format_patch to fail outright on Windows, where git would have succeeded.
  • Very long subjects produced excessively long filenames that could exceed filesystem limits; git truncates them.

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:

  • Use stdout=True and write the patch to a destination they control, rather than letting format_patch choose the filename.
  • Validate the chosen path before opening - e.g. compare os.path.realpath(returned_path) against os.path.realpath(outdir) and reject any patch whose resolved path is not inside outdir.
  • Pre-screen commits and refuse to format any whose subject's first line contains /, \, .., or other characters that are not safe on the target filesystem.

Resources

  • Fix commit: https://github.com/jelmer/dulwich/commit/c2446e51b
  • Affected API: dulwich.porcelain.format_patch / dulwich format-patch
  • Reference behavior: git's format_sanitized_subject in pretty.c

Basic information

Type
reviewed
Severity
low
Advisory on GitHub
Open advisory ↗
Repository advisory
Open repository advisory ↗
Source code
Browse source ↗
Published (advisory)
2026-06-08 23:04:48 UTC
Updated
2026-06-11 14:07:17 UTC
GitHub reviewed
2026-06-08 23:04:48 UTC
NVD published
2026-06-10

EPSS Score

Score Percentile
0.01% 1.12%

CVSS Scores

Base score Version Severity Vector
3.3 3.1
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N Click to expand
Attack vector (AV:L)
They already need access on the box, or another person has to do something wrong; it’s not a remote drive-by.
Attack complexity (AC:L)
Once they can reach the bug, pulling it off is straightforward—no weird race conditions or rare setup.
Privileges required (PR:N)
No account or special rights needed—anonymous or random user is enough.
User interaction (UI:R)
A real person has to do something—click, install, enable—otherwise it doesn’t land.
Scope (S:U)
Damage stays in the same “trust bubble” as the broken component—no big spill into unrelated systems.
Confidentiality (C:N)
Doesn’t really leak secrets in a meaningful way.
Integrity (I:L)
Attackers could change some data, but it’s limited—not everything goes.
Availability (A:N)
Service keeps running; no real outage angle.

Identifiers

CWEs

CWE id Name
CWE-22 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

Credits

  • ctoth (reporter)
  • jelmer (remediation_developer)

Affected packages (1)

Vulnerable version ranges and first patched releases as published by GitHub.

Ecosystem Package Vulnerable range First patched Vulnerable functions
pip dulwich >= 0.24.0, < 1.2.5 1.2.5

References

cvelogic Threat Intelligence