Skip to content
Heist/ feed
Back to feed
HighFix available

Dulwich: Symlink directory traversal in filter-branch index_filter via cross-commit state persistence

Type: advisorySources: OSV

## Summary Dulwich's `filter_branch.py` `CommitFilter._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()` at `dulwich/filter_branch.py:212` calls `build_index_from_tree(".", tmp_index_path, ...)` which materializes all tree entries to the current working directory. The `finally` block (line 229-230) only cleans up the temporary index file (`os.unlink(tmp_index_path)`) — NOT the filesystem files written to CWD. When `process_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, and `validate_path_element` only validates name patterns, not filesystem state. ## Impact An attacker can craft a malicious repository where running `filter_branch` with 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 1. Attacker creates a repository where commit history (linearized) has: - Ancestor commit: tree entry `evil` (mode 120000, symlink → `/target_dir`) - Descendant commit: tree entry `evil/pa

Products
dulwich
Affected versions
0.23.1, 0.23.2, 0.24.0, 0.24.1, 0.24.10, 0.24.2, 0.24.3, 0.24.4, 0.24.5, 0.24.6, 0.24.7, 0.24.8, 0.24.9, 0.25.0, 0.25.1, 0.25.2, 1.0.0, 1.1.0, 1.2.0, 1.2.1, 1.2.2, 1.2.3, 1.2.4, 1.2.5, 1.2.6, 1.2.7
Fixed versions
1.2.8
CVEs
CVE-2026-114097
Confirmed fixed CVEs
Not specified
Updated in this feed
31 Oct 2026, 14:09 UTC · Revision 1

Source evidence

Dulwich: Symlink directory traversal in filter-branch index_filter via cross-commit state persistence

core-osv · Original source

Published 2 Oct 2026, 18:53 UTC

## Summary Dulwich's `filter_branch.py` `CommitFilter._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()` at `dulwich/filter_branch.py:212` calls `build_index_from_tree(".", tmp_index_path, ...)` which materializes all tree entries to the current working directory. The `finally` block (line 229-230) only cleans up the temporary index file (`os.unlink(tmp_index_path)`) — NOT the filesystem files written to CWD. When `process_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, and `validate_path_element` only validates name patterns, not filesystem state. ## Impact An attacker can craft a malicious repository where running `filter_branch` with 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 1. Attacker creates a repository where commit history (linearized) has: - Ancestor commit: tree entry `evil` (mode 120000, symlink → `/target_dir`) - Descendant commit: tree entry `evil/pa

osv:GHSA-5fqc-mrg8-w798

dulwich

Fixed versions: 1.2.8

Complete source record
{
  "id": "740bbc5db4f2939d75b23e276c794af2ba115d5a293d24ea8f7cce954dc99404",
  "url": "https://osv.dev/vulnerability/GHSA-5fqc-mrg8-w798",
  "method": "structured",
  "payload": {
    "kind": "advisory",
    "title": "Dulwich: Symlink directory traversal in filter-branch index_filter via cross-commit state persistence",
    "summary": "## Summary Dulwich's `filter_branch.py` `CommitFilter._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()` at `dulwich/filter_branch.py:212` calls `build_index_from_tree(\".\", tmp_index_path, ...)` which materializes all tree entries to the current working directory. The `finally` block (line 229-230) only cleans up the temporary index file (`os.unlink(tmp_index_path)`) — NOT the filesystem files written to CWD. When `process_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, and `validate_path_element` only validates name patterns, not filesystem state. ## Impact An attacker can craft a malicious repository where running `filter_branch` with 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 1. Attacker creates a repository where commit history (linearized) has: - Ancestor commit: tree entry `evil` (mode 120000, symlink → `/target_dir`) - Descendant commit: tree entry `evil/pa",
    "evidence": "osv:GHSA-5fqc-mrg8-w798",
    "products": [
      {
        "name": {
          "value": "dulwich",
          "evidence": "/affected/0/package/name"
        },
        "prerequisites": [],
        "fixed_versions": [
          {
            "value": "1.2.8",
            "evidence": "/affected/0/ranges"
          }
        ],
        "affected_versions": [
          {
            "value": "0.23.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.23.2",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.24.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.24.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.24.10",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.24.2",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.24.3",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.24.4",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.24.5",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.24.6",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.24.7",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.24.8",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.24.9",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.25.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.25.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "0.25.2",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "1.0.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "1.1.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "1.2.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "1.2.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "1.2.2",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "1.2.3",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "1.2.4",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "1.2.5",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "1.2.6",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "1.2.7",
            "evidence": "/affected/0/versions"
          }
        ]
      }
    ],
    "severity": "high",
    "structured": {
      "id": "GHSA-5fqc-mrg8-w798",
      "aliases": [
        "CVE-2026-114097",
        "GHSA-5fqc-mrg8-w798"
      ],
      "metrics": [
        {
          "type": "CVSS_V3",
          "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H"
        }
      ],
      "affected": [
        {
          "ranges": [
            {
              "type": "ECOSYSTEM",
              "events": [
                {
                  "introduced": "0.23.1"
                },
                {
                  "fixed": "1.2.8"
                }
              ]
            }
          ],
          "package": {
            "name": "dulwich",
            "purl": "pkg:pypi/dulwich",
            "ecosystem": "PyPI"
          },
          "versions": [
            "0.23.1",
            "0.23.2",
            "0.24.0",
            "0.24.1",
            "0.24.10",
            "0.24.2",
            "0.24.3",
            "0.24.4",
            "0.24.5",
            "0.24.6",
            "0.24.7",
            "0.24.8",
            "0.24.9",
            "0.25.0",
            "0.25.1",
            "0.25.2",
            "1.0.0",
            "1.1.0",
            "1.2.0",
            "1.2.1",
            "1.2.2",
            "1.2.3",
            "1.2.4",
            "1.2.5",
            "1.2.6",
            "1.2.7"
          ],
          "database_specific": {
            "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-5fqc-mrg8-w798/GHSA-5fqc-mrg8-w798.json",
            "last_known_affected_version_range": "<= 1.2.7"
          }
        }
      ],
      "modified": "2026-10-21T10:57:27.569314986Z",
      "provider": "osv",
      "references": [
        "https://github.com/jelmer/dulwich/security/advisories/GHSA-5fqc-mrg8-w798",
        "https://github.com/jelmer/dulwich/commit/9571ac60b851fce228dae7ededb380c6ce9b3fb8",
        "https://github.com/jelmer/dulwich",
        "https://github.com/jelmer/dulwich/releases/tag/dulwich-1.2.8"
      ],
      "provider_metadata": {
        "cwe_ids": [
          "CWE-22",
          "CWE-59"
        ],
        "severity": "HIGH",
        "github_reviewed": true,
        "nvd_published_at": null,
        "github_reviewed_at": "2026-10-02T18:53:05Z"
      }
    },
    "related_cves": [
      {
        "value": "CVE-2026-114097",
        "evidence": "/aliases"
      }
    ],
    "security_relevant": true,
    "severity_evidence": "/database_specific/severity"
  },
  "source_id": "core-osv",
  "document_id": 392986,
  "published_at": "2026-10-02T18:53:05+00:00"
}