diff options
author | Sebastian Thiel <sebastian.thiel@icloud.com> | 2022-12-29 08:09:28 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-29 08:09:28 +0100 |
commit | 678a8fe08dd466fcfe8676294b52887955138960 (patch) | |
tree | e0cb96633dc1c1cf8c362e2097140d75f60447f6 /git/cmd.py | |
parent | ae6a6e4b088a35c0fc7b17940722c8a515f7bee7 (diff) | |
parent | f4f2658d5d308b3fb9162e50cd4c7b346e7a0a47 (diff) | |
download | gitpython-678a8fe08dd466fcfe8676294b52887955138960.tar.gz |
Merge pull request #1521 from stsewd/block-insecure-options
Block insecure options and protocols by default
Diffstat (limited to 'git/cmd.py')
-rw-r--r-- | git/cmd.py | 53 |
1 files changed, 49 insertions, 4 deletions
@@ -4,6 +4,7 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php from __future__ import annotations +import re from contextlib import contextmanager import io import logging @@ -24,7 +25,7 @@ from git.compat import ( from git.exc import CommandError from git.util import is_cygwin_git, cygpath, expand_path, remove_password_if_present -from .exc import GitCommandError, GitCommandNotFound +from .exc import GitCommandError, GitCommandNotFound, UnsafeOptionError, UnsafeProtocolError from .util import ( LazyMixin, stream_copy, @@ -262,6 +263,8 @@ class Git(LazyMixin): _excluded_ = ("cat_file_all", "cat_file_header", "_version_info") + re_unsafe_protocol = re.compile("(.+)::.+") + def __getstate__(self) -> Dict[str, Any]: return slots_to_dict(self, exclude=self._excluded_) @@ -454,6 +457,48 @@ class Git(LazyMixin): url = url.replace("\\\\", "\\").replace("\\", "/") return url + @classmethod + def check_unsafe_protocols(cls, url: str) -> None: + """ + Check for unsafe protocols. + + Apart from the usual protocols (http, git, ssh), + Git allows "remote helpers" that have the form `<transport>::<address>`, + one of these helpers (`ext::`) can be used to invoke any arbitrary command. + + See: + + - https://git-scm.com/docs/gitremote-helpers + - https://git-scm.com/docs/git-remote-ext + """ + match = cls.re_unsafe_protocol.match(url) + if match: + protocol = match.group(1) + raise UnsafeProtocolError( + f"The `{protocol}::` protocol looks suspicious, use `allow_unsafe_protocols=True` to allow it." + ) + + @classmethod + def check_unsafe_options(cls, options: List[str], unsafe_options: List[str]) -> None: + """ + Check for unsafe options. + + Some options that are passed to `git <command>` can be used to execute + arbitrary commands, this are blocked by default. + """ + # Options can be of the form `foo` or `--foo bar` `--foo=bar`, + # so we need to check if they start with "--foo" or if they are equal to "foo". + bare_unsafe_options = [ + option.lstrip("-") + for option in unsafe_options + ] + for option in options: + for unsafe_option, bare_option in zip(unsafe_options, bare_unsafe_options): + if option.startswith(unsafe_option) or option == bare_option: + raise UnsafeOptionError( + f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it." + ) + class AutoInterrupt(object): """Kill/Interrupt the stored process instance once this instance goes out of scope. It is used to prevent processes piling up in case iterators stop reading. @@ -1148,12 +1193,12 @@ class Git(LazyMixin): return args @classmethod - def __unpack_args(cls, arg_list: Sequence[str]) -> List[str]: + def _unpack_args(cls, arg_list: Sequence[str]) -> List[str]: outlist = [] if isinstance(arg_list, (list, tuple)): for arg in arg_list: - outlist.extend(cls.__unpack_args(arg)) + outlist.extend(cls._unpack_args(arg)) else: outlist.append(str(arg_list)) @@ -1238,7 +1283,7 @@ class Git(LazyMixin): # Prepare the argument list opt_args = self.transform_kwargs(**opts_kwargs) - ext_args = self.__unpack_args([a for a in args if a is not None]) + ext_args = self._unpack_args([a for a in args if a is not None]) if insert_after_this_arg is None: args_list = opt_args + ext_args |