| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,7 @@ | |||
| 6 | 6 | import gc | |
| 7 | 7 | from io import BytesIO | |
| 8 | 8 | import logging | |
| 9 | + import ntpath | ||
| 9 | 10 | import os | |
| 10 | 11 | import os.path as osp | |
| 11 | 12 | import stat | |
@@ -302,10 +303,21 @@ def _config_parser_constrained(self, read_only: bool) -> SectionConstraint: | |||
| 302 | 303 | parser.set_submodule(self) | |
| 303 | 304 | return SectionConstraint(parser, sm_section(self.name)) | |
| 304 | 305 | ||
| 306 | + @classmethod | ||
| 307 | + def _validated_name(cls, name: str) -> str: | ||
| 308 | + if ( | ||
| 309 | + not name | ||
| 310 | + or name.startswith(("/", "\\")) | ||
| 311 | + or ntpath.splitdrive(name)[0] | ||
| 312 | + or ".." in name.replace("\\", "/").split("/") | ||
| 313 | + ): | ||
| 314 | + raise ValueError("Invalid submodule name %r" % name) | ||
| 315 | + return name | ||
| 316 | + | ||
| 305 | 317 | @classmethod | |
| 306 | 318 | def _module_abspath(cls, parent_repo: "Repo", path: PathLike, name: str) -> PathLike: | |
| 307 | 319 | if cls._need_gitfile_submodules(parent_repo.git): | |
| 308 | - return osp.join(parent_repo.git_dir, "modules", name) | ||
| 320 | + return osp.join(parent_repo.git_dir, "modules", cls._validated_name(name)) | ||
| 309 | 321 | if parent_repo.working_tree_dir: | |
| 310 | 322 | return osp.join(parent_repo.working_tree_dir, path) | |
| 311 | 323 | raise NotADirectoryError() | |
@@ -523,6 +535,7 @@ def add( | |||
| 523 | 535 | raise InvalidGitRepositoryError("Cannot add submodules to bare repositories") | |
| 524 | 536 | # END handle bare repos | |
| 525 | 537 | ||
| 538 | + cls._validated_name(name) | ||
| 526 | 539 | path = cls._to_relative_path(repo, path) | |
| 527 | 540 | ||
| 528 | 541 | # Ensure we never put backslashes into the URL, as might happen on Windows. | |
@@ -771,6 +784,8 @@ def fetch_remotes(module_repo: "Repo") -> None: | |||
| 771 | 784 | # END fetch new data | |
| 772 | 785 | ||
| 773 | 786 | try: | |
| 787 | + self._validated_name(self.name) | ||
| 788 | + | ||
| 774 | 789 | # ENSURE REPO IS PRESENT AND UP-TO-DATE | |
| 775 | 790 | ####################################### | |
| 776 | 791 | try: | |
@@ -1020,6 +1035,7 @@ def move(self, module_path: PathLike, configuration: bool = True, module: bool = | |||
| 1020 | 1035 | raise ValueError("You must specify to move at least the module or the configuration of the submodule") | |
| 1021 | 1036 | # END handle input | |
| 1022 | 1037 | ||
| 1038 | + self._validated_name(self.name) | ||
| 1023 | 1039 | module_checkout_path = self._to_relative_path(self.repo, module_path) | |
| 1024 | 1040 | ||
| 1025 | 1041 | # VERIFY DESTINATION | |
@@ -1160,6 +1176,7 @@ def remove( | |||
| 1160 | 1176 | raise ValueError("Need to specify to delete at least the module, or the configuration") | |
| 1161 | 1177 | # END handle parameters | |
| 1162 | 1178 | ||
| 1179 | + self._validated_name(self.name) | ||
| 1163 | 1180 | # Recursively remove children of this submodule. | |
| 1164 | 1181 | nc = 0 | |
| 1165 | 1182 | for csm in self.children(): | |
@@ -1416,6 +1433,9 @@ def rename(self, new_name: str) -> "Submodule": | |||
| 1416 | 1433 | if self.name == new_name: | |
| 1417 | 1434 | return self | |
| 1418 | 1435 | ||
| 1436 | + self._validated_name(self.name) | ||
| 1437 | + self._validated_name(new_name) | ||
| 1438 | + | ||
| 1419 | 1439 | # .git/config | |
| 1420 | 1440 | with self.repo.config_writer() as pw: | |
| 1421 | 1441 | # As we ourselves didn't write anything about submodules into the parent | |
@@ -1463,6 +1483,7 @@ def module(self) -> "Repo": | |||
| 1463 | 1483 | If a repository was not available. | |
| 1464 | 1484 | This could also mean that it was not yet initialized. | |
| 1465 | 1485 | """ | |
| 1486 | + self._validated_name(self.name) | ||
| 1466 | 1487 | module_checkout_abspath = self.abspath | |
| 1467 | 1488 | try: | |
| 1468 | 1489 | repo = git.Repo(module_checkout_abspath) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -925,6 +925,75 @@ def test_update_submodule_with_relative_path(self, rwdir): | |||
| 925 | 925 | ||
| 926 | 926 | cloned_repo.submodule_update(init=True, recursive=True) | |
| 927 | 927 | ||
| 928 | + @with_rw_directory | ||
| 929 | + @_patch_git_config("protocol.file.allow", "always") | ||
| 930 | + def test_update_rejects_parent_component_in_name(self, rwdir): | ||
| 931 | + source = git.Repo.init(osp.join(rwdir, "source")) | ||
| 932 | + source.git.commit(m="initial commit", allow_empty=True) | ||
| 933 | + | ||
| 934 | + parent = git.Repo.init(osp.join(rwdir, "parent")) | ||
| 935 | + parent.git.submodule("add", source.working_tree_dir, "module") | ||
| 936 | + parent.index.commit("add submodule") | ||
| 937 | + modules_file = Path(parent.working_tree_dir) / ".gitmodules" | ||
| 938 | + modules_file.write_text( | ||
| 939 | + modules_file.read_text().replace('submodule "module"', 'submodule "../../../escaped/module"') | ||
| 940 | + ) | ||
| 941 | + parent.index.add([".gitmodules"]) | ||
| 942 | + parent.index.commit("change submodule name") | ||
| 943 | + | ||
| 944 | + clone = git.Repo.clone_from(parent.working_tree_dir, osp.join(rwdir, "clone")) | ||
| 945 | + with pytest.raises(ValueError, match="submodule name"): | ||
| 946 | + clone.submodules[0].update(init=True) | ||
| 947 | + assert not osp.exists(osp.join(rwdir, "escaped")) | ||
| 948 | + | ||
| 949 | + Path(rwdir, "escaped").mkdir() | ||
| 950 | + git.Repo.clone_from( | ||
| 951 | + source.working_tree_dir, | ||
| 952 | + osp.join(clone.working_tree_dir, "module"), | ||
| 953 | + separate_git_dir=osp.join(rwdir, "escaped", "module"), | ||
| 954 | + ) | ||
| 955 | + with pytest.raises(ValueError, match="submodule name"): | ||
| 956 | + clone.submodules[0].update(init=True) | ||
| 957 | + | ||
| 958 | + invalid_names = ( | ||
| 959 | + "", | ||
| 960 | + "..", | ||
| 961 | + "../module", | ||
| 962 | + R"..\module", | ||
| 963 | + "nested/../module", | ||
| 964 | + R"nested\..\module", | ||
| 965 | + "/module", | ||
| 966 | + R"\module", | ||
| 967 | + "C:module", | ||
| 968 | + R"C:\module", | ||
| 969 | + ) | ||
| 970 | + for name in invalid_names: | ||
| 971 | + with pytest.raises(ValueError, match="submodule name"): | ||
| 972 | + Submodule._module_abspath(clone, "module", name) | ||
| 973 | + | ||
| 974 | + @with_rw_directory | ||
| 975 | + @_patch_git_config("protocol.file.allow", "always") | ||
| 976 | + def test_root_update_keeps_going_after_invalid_submodule_name(self, rwdir): | ||
| 977 | + source = git.Repo.init(osp.join(rwdir, "source")) | ||
| 978 | + source.git.commit(m="initial commit", allow_empty=True) | ||
| 979 | + | ||
| 980 | + parent = git.Repo.init(osp.join(rwdir, "parent")) | ||
| 981 | + parent.git.submodule("add", source.working_tree_dir, "invalid") | ||
| 982 | + parent.git.submodule("add", source.working_tree_dir, "valid") | ||
| 983 | + modules_file = Path(parent.working_tree_dir) / ".gitmodules" | ||
| 984 | + modules_file.write_text(modules_file.read_text().replace('submodule "invalid"', 'submodule "../invalid"')) | ||
| 985 | + parent.index.add([".gitmodules"]) | ||
| 986 | + parent.index.commit("add submodules") | ||
| 987 | + | ||
| 988 | + clone = git.Repo.clone_from(parent.working_tree_dir, osp.join(rwdir, "clone")) | ||
| 989 | + assert [sm.name for sm in clone.submodules] == ["../invalid", "valid"] | ||
| 990 | + | ||
| 991 | + clone.submodule_update(keep_going=True) | ||
| 992 | + | ||
| 993 | + assert os.listdir(osp.join(clone.working_tree_dir, "invalid")) == [] | ||
| 994 | + assert not clone.submodule("../invalid").module_exists() | ||
| 995 | + assert clone.submodule("valid").module_exists() | ||
| 996 | + | ||
| 928 | 997 | @with_rw_directory | |
| 929 | 998 | @_patch_git_config("protocol.file.allow", "always") | |
| 930 | 999 | def test_list_only_valid_submodules(self, rwdir): | |
| Back | FazBrowse Home | New Git URL |
0 commit comments