Skip to content

Commit c96bbe3

Browse files
codexByron
authored andcommitted
Reject submodule checkout paths outside the repository
GHSA-59cr-6r3x-644w identifies that submodule update paths could reach filesystem operations without the containment check already used by add and move. Add a regression that proves update rejects a parent-directory checkout path before cloning, and override Submodule.abspath to apply the shared _to_relative_path guard for every filesystem consumer. Git baseline: git.git read-cache.c verify_path_internal() rejects invalid index paths, covered for parent traversal by t/t9300-fast-import.sh. Validation: .venv/bin/python -m pytest test/test_submodule.py -q; pre-commit run --files git/objects/submodule/base.py test/test_submodule.py; mypy git/objects/submodule/base.py; git diff --check.
1 parent 1d47514 commit c96bbe3

2 files changed

Lines changed: 20 additions & 0 deletions

File tree

git/objects/submodule/base.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,11 @@ def _to_relative_path(cls, parent_repo: "Repo", path: PathLike) -> PathLike:
414414

415415
return path
416416

417+
@property
418+
def abspath(self) -> PathLike:
419+
self._to_relative_path(self.repo, self.path)
420+
return super().abspath
421+
417422
@classmethod
418423
def _write_git_file_and_module_config(cls, working_tree_dir: PathLike, module_abspath: PathLike) -> None:
419424
"""Write a ``.git`` file containing a (preferably) relative path to the actual

test/test_submodule.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,21 @@ class Repo:
13691369
osp.join(Repo.working_tree_dir + "-other", "module"),
13701370
)
13711371

1372+
@with_rw_directory
1373+
def test_update_rejects_checkout_path_outside_parent(self, rwdir):
1374+
parent = git.Repo.init(osp.join(rwdir, "parent"))
1375+
submodule = Submodule(
1376+
parent,
1377+
Submodule.NULL_BIN_SHA,
1378+
name="module",
1379+
path=osp.join("..", "outside"),
1380+
url="unused",
1381+
)
1382+
1383+
with mock.patch.object(Submodule, "_clone_repo", side_effect=AssertionError("clone attempted")):
1384+
with pytest.raises(ValueError, match="is not in repository"):
1385+
submodule.update(init=True)
1386+
13721387
@skipUnless(sys.platform == "win32", "Specifically for Windows.")
13731388
@with_rw_directory
13741389
def test_to_relative_path_windows_path_kinds(self, rwdir):

0 commit comments

Comments
 (0)