Skip to content

Commit 5326ebf

Browse files
codexByron
authored andcommitted
fix: ignore includes in submodule configuration
Submodule configuration is read from .gitmodules, whose contents may come from an untrusted repository. Its parser inherited merge_includes=True and could therefore open files named by include directives during ordinary submodule enumeration. Disable include merging at the SubmoduleConfigParser construction site. This matches Repo.config_writer() hardening from 41ecc6a and addresses GHSA-7833-fr7j-v32q without changing include behavior for trusted config parsers. The regression points .gitmodules at a non-config file and verifies the submodule entry remains readable without opening the included path. Validation: - pytest -q test/test_submodule.py::TestSubmodule::test_gitmodules_does_not_merge_includes - pytest -q - ruff check git/objects/submodule/base.py test/test_submodule.py - ruff format --check git/objects/submodule/base.py test/test_submodule.py
1 parent bde8148 commit 5326ebf

2 files changed

Lines changed: 17 additions & 1 deletion

File tree

git/objects/submodule/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ def _config_parser(
270270
raise ValueError("Cannot write blobs of 'historical' submodule configurations")
271271
# END handle writes of historical submodules
272272

273-
return SubmoduleConfigParser(fp_module, read_only=read_only)
273+
return SubmoduleConfigParser(fp_module, read_only=read_only, merge_includes=False)
274274

275275
def _clear_cache(self) -> None:
276276
"""Clear the possibly changed values."""

test/test_submodule.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,22 @@ def test_ignore_non_submodule_file(self, rwdir):
12071207

12081208
assert len(parent.submodules) == 0
12091209

1210+
@with_rw_directory
1211+
def test_gitmodules_does_not_merge_includes(self, rwdir):
1212+
parent = git.Repo.init(rwdir)
1213+
secret_path = osp.join(rwdir, "secret")
1214+
with open(secret_path, "w", encoding="utf-8") as secret:
1215+
secret.write("not git config\n")
1216+
with open(osp.join(rwdir, ".gitmodules"), "w", encoding="utf-8") as modules:
1217+
modules.write('[submodule "module"]\n')
1218+
modules.write("\tpath = module\n")
1219+
modules.write("\turl = https://example.com/module.git\n")
1220+
modules.write("[include]\n")
1221+
modules.write("\tpath = %s\n" % secret_path)
1222+
1223+
parser = Submodule._config_parser(parent, None, read_only=True)
1224+
self.assertEqual(parser.get_value('submodule "module"', "path"), "module")
1225+
12101226
@with_rw_directory
12111227
def test_remove_norefs(self, rwdir):
12121228
parent = git.Repo.init(osp.join(rwdir, "parent"))

0 commit comments

Comments
 (0)