diff mbox series

[wrynose,3/4] python3-git: fix CVE-2026-44243

Message ID 20260819050808.3986732-3-dkelaiya@cisco.com
State New
Headers show
Series [wrynose,1/4] python3-git: fix CVE-2026-42284 | expand

Commit Message

From: Darsh Kelaiya <dkelaiya@cisco.com>

This patch applies the upstream fix as referenced in [3], using all the
backported commits shown in [1] and [2].

[1] https://github.com/gitpython-developers/GitPython/commit/25ba54dd3fb374b8fade7de4be1ac2ac84722190
[2] https://github.com/gitpython-developers/GitPython/commit/4af8463cca31c2369312fcaa5309dfc30756c7b6
[3] https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-7545-fcxq-7j24

Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
 .../python3-git/CVE-2026-44243_p1.patch       | 134 ++++++++++++++++++
 .../python3-git/CVE-2026-44243_p2.patch       |  83 +++++++++++
 .../python/python3-git_3.1.43.bb              |   2 +
 3 files changed, 219 insertions(+)
 create mode 100644 meta/recipes-devtools/python/python3-git/CVE-2026-44243_p1.patch
 create mode 100644 meta/recipes-devtools/python/python3-git/CVE-2026-44243_p2.patch
diff mbox series

Patch

diff --git a/meta/recipes-devtools/python/python3-git/CVE-2026-44243_p1.patch b/meta/recipes-devtools/python/python3-git/CVE-2026-44243_p1.patch
new file mode 100644
index 0000000000..7eaaf703db
--- /dev/null
+++ b/meta/recipes-devtools/python/python3-git/CVE-2026-44243_p1.patch
@@ -0,0 +1,134 @@ 
+From 84b84e90d1ce0b35d627bee6c65f3218c72a53f5 Mon Sep 17 00:00:00 2001
+From: "GPT 5.5" <codex@openai.com>
+Date: Tue, 28 Apr 2026 09:17:31 +0800
+Subject: [PATCH] prevent out-of-repo access when manipulating references.
+
+This previously made it possible to create, modify and delete files outside outside
+of the repository, which is a problem if inputs aren't trusted.
+
+CVE: CVE-2026-44243
+Upstream-Status: Backport [https://github.com/gitpython-developers/GitPython/commit/25ba54dd3fb374b8fade7de4be1ac2ac84722190]
+
+Backport Changes:
+- Omitted test/test_refs.py because the PyPI 3.1.43 source used by
+  the recipe does not ship the upstream test tree.
+
+Co-authored-by: Sebastian Thiel <sebastian.thiel@icloud.com>
+(cherry picked from commit 25ba54dd3fb374b8fade7de4be1ac2ac84722190)
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ git/refs/log.py      |  2 +-
+ git/refs/remote.py   |  5 +++--
+ git/refs/symbolic.py | 37 +++++++++++++++++++++++++++++++------
+ 3 files changed, 35 insertions(+), 9 deletions(-)
+
+diff --git a/git/refs/log.py b/git/refs/log.py
+index 17e3a94b..88906758 100644
+--- a/git/refs/log.py
++++ b/git/refs/log.py
+@@ -213,7 +213,7 @@ class RefLog(List[RefLogEntry], Serializable):
+         :param ref:
+             :class:`~git.refs.symbolic.SymbolicReference` instance
+         """
+-        return osp.join(ref.repo.git_dir, "logs", to_native_path(ref.path))
++        return to_native_path(ref._get_validated_reflog_path(ref.repo, ref.path))
+ 
+     @classmethod
+     def iter_entries(cls, stream: Union[str, "BytesIO", mmap]) -> Iterator[RefLogEntry]:
+diff --git a/git/refs/remote.py b/git/refs/remote.py
+index b4f4f7b3..8244470b 100644
+--- a/git/refs/remote.py
++++ b/git/refs/remote.py
+@@ -63,12 +63,13 @@ class RemoteReference(Head):
+         # generally ignored in the refs/ folder. We don't though and delete remainders
+         # manually.
+         for ref in refs:
++            cls._check_ref_name_valid(ref.path)
+             try:
+-                os.remove(os.path.join(repo.common_dir, ref.path))
++                os.remove(cls._get_validated_path(repo.common_dir, ref.path))
+             except OSError:
+                 pass
+             try:
+-                os.remove(os.path.join(repo.git_dir, ref.path))
++                os.remove(cls._get_validated_path(repo.git_dir, ref.path))
+             except OSError:
+                 pass
+         # END for each ref
+diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py
+index 510850b2..ba24f2c2 100644
+--- a/git/refs/symbolic.py
++++ b/git/refs/symbolic.py
+@@ -109,6 +109,32 @@ class SymbolicReference:
+     def abspath(self) -> PathLike:
+         return join_path_native(_git_dir(self.repo, self.path), self.path)
+ 
++    @staticmethod
++    def _get_validated_path(base: PathLike, path: PathLike) -> str:
++        path = os.fspath(path)
++        base_path = os.path.realpath(os.fspath(base))
++        abs_path = os.path.realpath(os.path.join(base_path, path))
++        try:
++            common_path = os.path.commonpath([base_path, abs_path])
++        except ValueError as e:
++            raise ValueError("Reference path %r escapes the repository" % path) from e
++        if os.path.normcase(common_path) != os.path.normcase(base_path):
++            raise ValueError("Reference path %r escapes the repository" % path)
++        return abs_path
++
++    @classmethod
++    def _get_validated_ref_path(cls, repo: "Repo", path: PathLike) -> str:
++        """Return the absolute filesystem path for a ref after validating it."""
++        cls._check_ref_name_valid(path)
++        ref_path = os.fspath(path)
++        return cls._get_validated_path(_git_dir(repo, ref_path), ref_path)
++
++    @classmethod
++    def _get_validated_reflog_path(cls, repo: "Repo", path: PathLike) -> str:
++        """Return the absolute filesystem path for a reflog after validating it."""
++        cls._check_ref_name_valid(path)
++        return cls._get_validated_path(os.path.join(repo.git_dir, "logs"), path)
++
+     @classmethod
+     def _get_packed_refs_path(cls, repo: "Repo") -> str:
+         return os.path.join(repo.common_dir, "packed-refs")
+@@ -478,7 +504,7 @@ class SymbolicReference:
+             # END handle non-existing
+         # END retrieve old hexsha
+ 
+-        fpath = self.abspath
++        fpath = self._get_validated_ref_path(self.repo, self.path)
+         assure_directory_exists(fpath, is_file=True)
+ 
+         lfd = LockedFD(fpath)
+@@ -623,7 +649,7 @@ class SymbolicReference:
+             Alternatively the symbolic reference to be deleted.
+         """
+         full_ref_path = cls.to_full_path(path)
+-        abs_path = os.path.join(repo.common_dir, full_ref_path)
++        abs_path = cls._get_validated_ref_path(repo, full_ref_path)
+         if os.path.exists(abs_path):
+             os.remove(abs_path)
+         else:
+@@ -686,9 +712,8 @@ class SymbolicReference:
+         symbolic reference. Otherwise it will be resolved to the corresponding object
+         and a detached symbolic reference will be created instead.
+         """
+-        git_dir = _git_dir(repo, path)
+         full_ref_path = cls.to_full_path(path)
+-        abs_ref_path = os.path.join(git_dir, full_ref_path)
++        abs_ref_path = cls._get_validated_ref_path(repo, full_ref_path)
+ 
+         # Figure out target data.
+         target = reference
+@@ -780,8 +805,8 @@ class SymbolicReference:
+         if self.path == new_path:
+             return self
+ 
+-        new_abs_path = os.path.join(_git_dir(self.repo, new_path), new_path)
+-        cur_abs_path = os.path.join(_git_dir(self.repo, self.path), self.path)
++        new_abs_path = self._get_validated_ref_path(self.repo, new_path)
++        cur_abs_path = self._get_validated_ref_path(self.repo, self.path)
+         if os.path.isfile(new_abs_path):
+             if not force:
+                 # If they point to the same file, it's not an error.
diff --git a/meta/recipes-devtools/python/python3-git/CVE-2026-44243_p2.patch b/meta/recipes-devtools/python/python3-git/CVE-2026-44243_p2.patch
new file mode 100644
index 0000000000..04e83d3657
--- /dev/null
+++ b/meta/recipes-devtools/python/python3-git/CVE-2026-44243_p2.patch
@@ -0,0 +1,83 @@ 
+From 4ab42809cb34222b1c574c07e083a4008e97d8de Mon Sep 17 00:00:00 2001
+From: "GPT 5.5" <codex@openai.com>
+Date: Tue, 28 Apr 2026 09:30:41 +0800
+Subject: [PATCH] address review feedback and CI failures
+
+Consolidate follow-up fixes from review and CI:
+
+- fix lint and mypy issues in reference log path handling
+- validate remote reference paths before invoking git branch deletion
+- add symlink escape coverage where realpath resolves symlinks
+- ensure temporary test repositories release git resources during cleanup
+
+CVE: CVE-2026-44243
+Upstream-Status: Backport [https://github.com/gitpython-developers/GitPython/commit/4af8463cca31c2369312fcaa5309dfc30756c7b6]
+
+Backport Changes:
+- Omitted test/test_refs.py because the PyPI 3.1.43 source used by
+  the recipe does not ship the upstream test tree.
+
+Co-authored-by: Sebastian Thiel <sebastian.thiel@icloud.com>
+(cherry picked from commit 4af8463cca31c2369312fcaa5309dfc30756c7b6)
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ git/refs/log.py    | 4 +++-
+ git/refs/remote.py | 4 +++-
+ git/util.py        | 2 +-
+ 3 files changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/git/refs/log.py b/git/refs/log.py
+index 88906758..642b1825 100644
+--- a/git/refs/log.py
++++ b/git/refs/log.py
+@@ -4,7 +4,6 @@
+ __all__ = ["RefLog", "RefLogEntry"]
+ 
+ from mmap import mmap
+-import os.path as osp
+ import re
+ import time as _time
+ 
+@@ -212,6 +211,9 @@ class RefLog(List[RefLogEntry], Serializable):
+ 
+         :param ref:
+             :class:`~git.refs.symbolic.SymbolicReference` instance
++
++        :raise ValueError:
++            If `ref.path` is invalid or escapes the repository's reflog directory.
+         """
+         return to_native_path(ref._get_validated_reflog_path(ref.repo, ref.path))
+ 
+diff --git a/git/refs/remote.py b/git/refs/remote.py
+index 8244470b..e16ae70f 100644
+--- a/git/refs/remote.py
++++ b/git/refs/remote.py
+@@ -58,12 +58,14 @@ class RemoteReference(Head):
+             `kwargs` are given for comparability with the base class method as we
+             should not narrow the signature.
+         """
++        for ref in refs:
++            cls._check_ref_name_valid(ref.path)
++
+         repo.git.branch("-d", "-r", *refs)
+         # The official deletion method will ignore remote symbolic refs - these are
+         # generally ignored in the refs/ folder. We don't though and delete remainders
+         # manually.
+         for ref in refs:
+-            cls._check_ref_name_valid(ref.path)
+             try:
+                 os.remove(cls._get_validated_path(repo.common_dir, ref.path))
+             except OSError:
+diff --git a/git/util.py b/git/util.py
+index 8c1c2601..27b239ab 100644
+--- a/git/util.py
++++ b/git/util.py
+@@ -289,7 +289,7 @@ def join_path(a: PathLike, *p: PathLike) -> PathLike:
+ 
+ if sys.platform == "win32":
+ 
+-    def to_native_path_windows(path: PathLike) -> PathLike:
++    def to_native_path_windows(path: PathLike) -> str:
+         path = str(path)
+         return path.replace("/", "\\")
+ 
diff --git a/meta/recipes-devtools/python/python3-git_3.1.43.bb b/meta/recipes-devtools/python/python3-git_3.1.43.bb
index f7388e2bbb..bd2b113489 100644
--- a/meta/recipes-devtools/python/python3-git_3.1.43.bb
+++ b/meta/recipes-devtools/python/python3-git_3.1.43.bb
@@ -16,6 +16,8 @@  SRC_URI += "file://CVE-2026-42284.patch \
            file://CVE-2026-42215_p1.patch \
            file://CVE-2026-42215_p2.patch \
            file://CVE-2026-42215_p3.patch \
+           file://CVE-2026-44243_p1.patch \
+           file://CVE-2026-44243_p2.patch \
            "
 SRC_URI[sha256sum] = "35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"