| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -72,6 +72,9 @@ | |||
| 72 | 72 | See: https://git-scm.com/docs/git-config#_conditional_includes | |
| 73 | 73 | """ | |
| 74 | 74 | ||
| 75 | + UNSAFE_CONFIG_CHARS_RE = re.compile(r"[\r\n\x00]") | ||
| 76 | + """Characters that cannot be safely written in config names or values.""" | ||
| 77 | + | ||
| 75 | 78 | ||
| 76 | 79 | class MetaParserBuilder(abc.ABCMeta): # noqa: B024 | |
| 77 | 80 | """Utility class wrapping base-class methods into decorators that assure read-only | |
@@ -778,6 +781,7 @@ def _assure_writable(self, method_name: str) -> None: | |||
| 778 | 781 | ||
| 779 | 782 | def add_section(self, section: "cp._SectionName") -> None: | |
| 780 | 783 | """Assures added options will stay in order.""" | |
| 784 | + self._assure_config_name_safe(section, "section") | ||
| 781 | 785 | return super().add_section(section) | |
| 782 | 786 | ||
| 783 | 787 | @property | |
@@ -884,10 +888,14 @@ def _value_to_string(self, value: Union[str, bytes, int, float, bool]) -> str: | |||
| 884 | 888 | ||
| 885 | 889 | def _value_to_string_safe(self, value: Union[str, bytes, int, float, bool]) -> str: | |
| 886 | 890 | value_str = self._value_to_string(value) | |
| 887 | - if re.search(r"[\r\n\x00]", value_str): | ||
| 891 | + if UNSAFE_CONFIG_CHARS_RE.search(value_str): | ||
| 888 | 892 | raise ValueError("Git config values must not contain CR, LF, or NUL") | |
| 889 | 893 | return value_str | |
| 890 | 894 | ||
| 895 | + def _assure_config_name_safe(self, name: "cp._SectionName", label: str) -> None: | ||
| 896 | + if isinstance(name, str) and UNSAFE_CONFIG_CHARS_RE.search(name): | ||
| 897 | + raise ValueError("Git config %s names must not contain CR, LF, or NUL" % label) | ||
| 898 | + | ||
| 891 | 899 | @needs_values | |
| 892 | 900 | @set_dirty_and_flush_changes | |
| 893 | 901 | def set( | |
@@ -896,6 +904,8 @@ def set( | |||
| 896 | 904 | option: str, | |
| 897 | 905 | value: Union[str, bytes, int, float, bool, None] = None, | |
| 898 | 906 | ) -> None: | |
| 907 | + self._assure_config_name_safe(section, "section") | ||
| 908 | + self._assure_config_name_safe(option, "option") | ||
| 899 | 909 | if value is not None: | |
| 900 | 910 | value = self._value_to_string_safe(value) | |
| 901 | 911 | return super().set(section, option, value) | |
@@ -920,6 +930,8 @@ def set_value(self, section: str, option: str, value: Union[str, bytes, int, flo | |||
| 920 | 930 | :return: | |
| 921 | 931 | This instance | |
| 922 | 932 | """ | |
| 933 | + self._assure_config_name_safe(section, "section") | ||
| 934 | + self._assure_config_name_safe(option, "option") | ||
| 923 | 935 | value_str = self._value_to_string_safe(value) | |
| 924 | 936 | if not self.has_section(section): | |
| 925 | 937 | self.add_section(section) | |
@@ -948,6 +960,8 @@ def add_value(self, section: str, option: str, value: Union[str, bytes, int, flo | |||
| 948 | 960 | :return: | |
| 949 | 961 | This instance | |
| 950 | 962 | """ | |
| 963 | + self._assure_config_name_safe(section, "section") | ||
| 964 | + self._assure_config_name_safe(option, "option") | ||
| 951 | 965 | value_str = self._value_to_string_safe(value) | |
| 952 | 966 | if not self.has_section(section): | |
| 953 | 967 | self.add_section(section) | |
@@ -968,6 +982,7 @@ def rename_section(self, section: str, new_name: str) -> "GitConfigParser": | |||
| 968 | 982 | """ | |
| 969 | 983 | if not self.has_section(section): | |
| 970 | 984 | raise ValueError("Source section '%s' doesn't exist" % section) | |
| 985 | + self._assure_config_name_safe(new_name, "section") | ||
| 971 | 986 | if self.has_section(new_name): | |
| 972 | 987 | raise ValueError("Destination section '%s' already exists" % new_name) | |
| 973 | 988 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -163,6 +163,37 @@ def test_set_value_rejects_config_injection(self, rw_dir): | |||
| 163 | 163 | self.assertFalse(git_config.has_section("user")) | |
| 164 | 164 | self.assertFalse(git_config.has_section("core")) | |
| 165 | 165 | ||
| 166 | + @with_rw_directory | ||
| 167 | + def test_set_value_rejects_unsafe_section_and_option_names(self, rw_dir): | ||
| 168 | + config_path = osp.join(rw_dir, "config") | ||
| 169 | + bad_keys = ("user]\n[core", "user]\r[core", "user]\x00[core") | ||
| 170 | + | ||
| 171 | + with GitConfigParser(config_path, read_only=False) as git_config: | ||
| 172 | + git_config.add_section("user") | ||
| 173 | + for bad_key in bad_keys: | ||
| 174 | + with pytest.raises(ValueError, match="CR, LF, or NUL"): | ||
| 175 | + git_config.add_section(bad_key) | ||
| 176 | + with pytest.raises(ValueError, match="CR, LF, or NUL"): | ||
| 177 | + git_config.set(bad_key, "hooksPath", "/tmp/hooks") | ||
| 178 | + with pytest.raises(ValueError, match="CR, LF, or NUL"): | ||
| 179 | + git_config.set("user", bad_key, "/tmp/hooks") | ||
| 180 | + with pytest.raises(ValueError, match="CR, LF, or NUL"): | ||
| 181 | + git_config.set_value(bad_key, "hooksPath", "/tmp/hooks") | ||
| 182 | + with pytest.raises(ValueError, match="CR, LF, or NUL"): | ||
| 183 | + git_config.set_value("user", bad_key, "/tmp/hooks") | ||
| 184 | + with pytest.raises(ValueError, match="CR, LF, or NUL"): | ||
| 185 | + git_config.add_value(bad_key, "hooksPath", "/tmp/hooks") | ||
| 186 | + with pytest.raises(ValueError, match="CR, LF, or NUL"): | ||
| 187 | + git_config.add_value("user", bad_key, "/tmp/hooks") | ||
| 188 | + with pytest.raises(ValueError, match="CR, LF, or NUL"): | ||
| 189 | + git_config.rename_section("user", bad_key) | ||
| 190 | + | ||
| 191 | + git_config.set_value("user", "name", "safe") | ||
| 192 | + | ||
| 193 | + with GitConfigParser(config_path, read_only=True) as git_config: | ||
| 194 | + self.assertEqual(git_config.get_value("user", "name"), "safe") | ||
| 195 | + self.assertFalse(git_config.has_section("core")) | ||
| 196 | + | ||
| 166 | 197 | @with_rw_directory | |
| 167 | 198 | def test_set_and_add_value_reject_unsafe_value_characters(self, rw_dir): | |
| 168 | 199 | config_path = osp.join(rw_dir, "config") | |
| Back | FazBrowse Home | New Git URL |
0 commit comments