summaryrefslogtreecommitdiff
path: root/git/cmd.py
diff options
context:
space:
mode:
authorSebastian Thiel <sebastian.thiel@icloud.com>2022-12-29 08:09:28 +0100
committerGitHub <noreply@github.com>2022-12-29 08:09:28 +0100
commit678a8fe08dd466fcfe8676294b52887955138960 (patch)
treee0cb96633dc1c1cf8c362e2097140d75f60447f6 /git/cmd.py
parentae6a6e4b088a35c0fc7b17940722c8a515f7bee7 (diff)
parentf4f2658d5d308b3fb9162e50cd4c7b346e7a0a47 (diff)
downloadgitpython-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.py53
1 files changed, 49 insertions, 4 deletions
diff --git a/git/cmd.py b/git/cmd.py
index 3dd5aad3..9ef1e3a6 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -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