| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -944,22 +944,34 @@ def check_unsafe_protocols(cls, url: str) -> None: | |||
| 944 | 944 | f"The `{protocol}::` protocol looks suspicious, use `allow_unsafe_protocols=True` to allow it." | |
| 945 | 945 | ) | |
| 946 | 946 | ||
| 947 | + @classmethod | ||
| 948 | + def _canonicalize_option_name(cls, option: str) -> str: | ||
| 949 | + """Return the option name used for unsafe-option checks. | ||
| 950 | + | ||
| 951 | + Examples: | ||
| 952 | + ``"--upload-pack=/tmp/helper"`` -> ``"upload-pack"`` | ||
| 953 | + ``"upload_pack"`` -> ``"upload-pack"`` | ||
| 954 | + ``"--config core.filemode=false"`` -> ``"config"`` | ||
| 955 | + """ | ||
| 956 | + option_name = option.lstrip("-").split("=", 1)[0] | ||
| 957 | + option_tokens = option_name.split(None, 1) | ||
| 958 | + if not option_tokens: | ||
| 959 | + return "" | ||
| 960 | + return dashify(option_tokens[0]) | ||
| 961 | + | ||
| 947 | 962 | @classmethod | |
| 948 | 963 | def check_unsafe_options(cls, options: List[str], unsafe_options: List[str]) -> None: | |
| 949 | 964 | """Check for unsafe options. | |
| 950 | 965 | ||
| 951 | 966 | Some options that are passed to ``git <command>`` can be used to execute | |
| 952 | 967 | arbitrary commands. These are blocked by default. | |
| 953 | 968 | """ | |
| 954 | - # Options can be of the form `foo`, `--foo bar`, or `--foo=bar`, so we need to | ||
| 955 | - # check if they start with "--foo" or if they are equal to "foo". | ||
| 956 | - bare_unsafe_options = [option.lstrip("-") for option in unsafe_options] | ||
| 969 | + # Options can be of the form `foo`, `--foo`, `--foo bar`, or `--foo=bar`. | ||
| 970 | + canonical_unsafe_options = {cls._canonicalize_option_name(option): option for option in unsafe_options} | ||
| 957 | 971 | for option in options: | |
| 958 | - for unsafe_option, bare_option in zip(unsafe_options, bare_unsafe_options): | ||
| 959 | - if option.startswith(unsafe_option) or option == bare_option: | ||
| 960 | - raise UnsafeOptionError( | ||
| 961 | - f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it." | ||
| 962 | - ) | ||
| 972 | + unsafe_option = canonical_unsafe_options.get(cls._canonicalize_option_name(option)) | ||
| 973 | + if unsafe_option is not None: | ||
| 974 | + raise UnsafeOptionError(f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it.") | ||
| 963 | 975 | ||
| 964 | 976 | AutoInterrupt: TypeAlias = _AutoInterrupt | |
| 965 | 977 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -128,6 +128,7 @@ def test_clone_unsafe_options(self, rw_repo): | |||
| 128 | 128 | ||
| 129 | 129 | unsafe_options = [ | |
| 130 | 130 | {"upload-pack": f"touch {tmp_file}"}, | |
| 131 | + {"upload_pack": f"touch {tmp_file}"}, | ||
| 131 | 132 | {"u": f"touch {tmp_file}"}, | |
| 132 | 133 | {"config": "protocol.ext.allow=always"}, | |
| 133 | 134 | {"c": "protocol.ext.allow=always"}, | |
@@ -216,6 +217,7 @@ def test_clone_from_unsafe_options(self, rw_repo): | |||
| 216 | 217 | ||
| 217 | 218 | unsafe_options = [ | |
| 218 | 219 | {"upload-pack": f"touch {tmp_file}"}, | |
| 220 | + {"upload_pack": f"touch {tmp_file}"}, | ||
| 219 | 221 | {"u": f"touch {tmp_file}"}, | |
| 220 | 222 | {"config": "protocol.ext.allow=always"}, | |
| 221 | 223 | {"c": "protocol.ext.allow=always"}, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -27,6 +27,7 @@ | |||
| 27 | 27 | import ddt | |
| 28 | 28 | ||
| 29 | 29 | from git import Git, GitCommandError, GitCommandNotFound, Repo, cmd, refresh | |
| 30 | + from git.exc import UnsafeOptionError | ||
| 30 | 31 | from git.util import cwd, finalize_process | |
| 31 | 32 | ||
| 32 | 33 | from test.lib import TestBase, fixture_path, with_rw_directory | |
@@ -154,6 +155,21 @@ def test_it_transforms_kwargs_into_git_command_arguments(self): | |||
| 154 | 155 | res = self.git.transform_kwargs(**{"s": True, "t": True}) | |
| 155 | 156 | self.assertEqual({"-s", "-t"}, set(res)) | |
| 156 | 157 | ||
| 158 | + def test_check_unsafe_options_normalizes_kwargs(self): | ||
| 159 | + cases = [ | ||
| 160 | + (["upload_pack"], ["--upload-pack"]), | ||
| 161 | + (["receive_pack"], ["--receive-pack"]), | ||
| 162 | + (["exec"], ["--exec"]), | ||
| 163 | + (["u"], ["-u"]), | ||
| 164 | + (["c"], ["-c"]), | ||
| 165 | + (["--upload-pack=/tmp/helper"], ["--upload-pack"]), | ||
| 166 | + (["--config core.filemode=false"], ["--config"]), | ||
| 167 | + ] | ||
| 168 | + | ||
| 169 | + for options, unsafe_options in cases: | ||
| 170 | + with self.assertRaises(UnsafeOptionError): | ||
| 171 | + Git.check_unsafe_options(options=options, unsafe_options=unsafe_options) | ||
| 172 | + | ||
| 157 | 173 | _shell_cases = ( | |
| 158 | 174 | # value_in_call, value_from_class, expected_popen_arg | |
| 159 | 175 | (None, False, False), | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -827,7 +827,7 @@ def test_fetch_unsafe_options(self, rw_repo): | |||
| 827 | 827 | remote = rw_repo.remote("origin") | |
| 828 | 828 | tmp_dir = Path(tdir) | |
| 829 | 829 | tmp_file = tmp_dir / "pwn" | |
| 830 | - unsafe_options = [{"upload-pack": f"touch {tmp_file}"}] | ||
| 830 | + unsafe_options = [{"upload-pack": f"touch {tmp_file}"}, {"upload_pack": f"touch {tmp_file}"}] | ||
| 831 | 831 | for unsafe_option in unsafe_options: | |
| 832 | 832 | with self.assertRaises(UnsafeOptionError): | |
| 833 | 833 | remote.fetch(**unsafe_option) | |
@@ -895,7 +895,7 @@ def test_pull_unsafe_options(self, rw_repo): | |||
| 895 | 895 | remote = rw_repo.remote("origin") | |
| 896 | 896 | tmp_dir = Path(tdir) | |
| 897 | 897 | tmp_file = tmp_dir / "pwn" | |
| 898 | - unsafe_options = [{"upload-pack": f"touch {tmp_file}"}] | ||
| 898 | + unsafe_options = [{"upload-pack": f"touch {tmp_file}"}, {"upload_pack": f"touch {tmp_file}"}] | ||
| 899 | 899 | for unsafe_option in unsafe_options: | |
| 900 | 900 | with self.assertRaises(UnsafeOptionError): | |
| 901 | 901 | remote.pull(**unsafe_option) | |
@@ -964,10 +964,9 @@ def test_push_unsafe_options(self, rw_repo): | |||
| 964 | 964 | tmp_dir = Path(tdir) | |
| 965 | 965 | tmp_file = tmp_dir / "pwn" | |
| 966 | 966 | unsafe_options = [ | |
| 967 | - { | ||
| 968 | - "receive-pack": f"touch {tmp_file}", | ||
| 969 | - "exec": f"touch {tmp_file}", | ||
| 970 | - } | ||
| 967 | + {"receive-pack": f"touch {tmp_file}"}, | ||
| 968 | + {"receive_pack": f"touch {tmp_file}"}, | ||
| 969 | + {"exec": f"touch {tmp_file}"}, | ||
| 971 | 970 | ] | |
| 972 | 971 | for unsafe_option in unsafe_options: | |
| 973 | 972 | assert not tmp_file.exists() | |
@@ -991,10 +990,9 @@ def test_push_unsafe_options_allowed(self, rw_repo): | |||
| 991 | 990 | tmp_dir = Path(tdir) | |
| 992 | 991 | tmp_file = tmp_dir / "pwn" | |
| 993 | 992 | unsafe_options = [ | |
| 994 | - { | ||
| 995 | - "receive-pack": f"touch {tmp_file}", | ||
| 996 | - "exec": f"touch {tmp_file}", | ||
| 997 | - } | ||
| 993 | + {"receive-pack": f"touch {tmp_file}"}, | ||
| 994 | + {"receive_pack": f"touch {tmp_file}"}, | ||
| 995 | + {"exec": f"touch {tmp_file}"}, | ||
| 998 | 996 | ] | |
| 999 | 997 | for unsafe_option in unsafe_options: | |
| 1000 | 998 | # The options will be allowed, but the command will fail. | |
| Back | FazBrowse Home | New Git URL |
0 commit comments