Skip to content

Commit 0709171

Browse files
codexByron
authored andcommitted
Reject submodule paths through outside symlinks
Resolve the worktree and checkout paths before applying containment so an in-repository symlink cannot redirect update or another filesystem consumer outside the repository. Add a regression covering update through a symlinked path component. 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 f67ab16 commit 0709171

2 files changed

Lines changed: 21 additions & 2 deletions

File tree

git/objects/submodule/base.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,11 @@ def _to_relative_path(cls, parent_repo: "Repo", path: PathLike) -> PathLike:
416416

417417
@property
418418
def abspath(self) -> PathLike:
419-
self._to_relative_path(self.repo, self.path)
420-
return super().abspath
419+
path = super().abspath
420+
root = self.repo.working_tree_dir
421+
assert root is not None
422+
_to_relative_path(osp.realpath(root), osp.realpath(path))
423+
return path
421424

422425
@classmethod
423426
def _write_git_file_and_module_config(cls, working_tree_dir: PathLike, module_abspath: PathLike) -> None:

test/test_submodule.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,22 @@ def test_update_rejects_checkout_path_outside_parent(self, rwdir):
13841384
with pytest.raises(ValueError, match="is not in repository"):
13851385
submodule.update(init=True)
13861386

1387+
@with_rw_directory
1388+
def test_update_rejects_checkout_path_through_outside_symlink(self, rwdir):
1389+
parent = git.Repo.init(osp.join(rwdir, "parent"))
1390+
os.symlink(osp.join(rwdir, "outside"), osp.join(parent.working_tree_dir, "link"))
1391+
submodule = Submodule(
1392+
parent,
1393+
Submodule.NULL_BIN_SHA,
1394+
name="module",
1395+
path=osp.join("link", "module"),
1396+
url="unused",
1397+
)
1398+
1399+
with mock.patch.object(Submodule, "_clone_repo", side_effect=AssertionError("clone attempted")):
1400+
with pytest.raises(ValueError, match="is not in repository"):
1401+
submodule.update(init=True)
1402+
13871403
@skipUnless(sys.platform == "win32", "Specifically for Windows.")
13881404
@with_rw_directory
13891405
def test_to_relative_path_windows_path_kinds(self, rwdir):

0 commit comments

Comments
 (0)