|
5 | 5 | # the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
6 | 6 |
|
7 | 7 | import contextlib
|
| 8 | +import re |
| 9 | + |
8 | 10 | import io
|
9 | 11 | import logging
|
10 | 12 | import os
|
|
32 | 34 | is_posix,
|
33 | 35 | is_win,
|
34 | 36 | )
|
35 |
| -from git.exc import CommandError |
| 37 | +from git.exc import CommandError, UnsafeOptionError, UnsafeProtocolError |
36 | 38 | from git.util import is_cygwin_git, cygpath, expand_path
|
37 | 39 |
|
38 | 40 | from .exc import (
|
@@ -172,9 +174,49 @@ class Git(LazyMixin):
|
172 | 174 |
|
173 | 175 | _excluded_ = ('cat_file_all', 'cat_file_header', '_version_info')
|
174 | 176 |
|
| 177 | + re_unsafe_protocol = re.compile("(.+)::.+") |
| 178 | + |
175 | 179 | def __getstate__(self):
|
176 | 180 | return slots_to_dict(self, exclude=self._excluded_)
|
177 | 181 |
|
| 182 | + @classmethod |
| 183 | + def check_unsafe_protocols(cls, url): |
| 184 | + """ |
| 185 | + Check for unsafe protocols. |
| 186 | + Apart from the usual protocols (http, git, ssh), |
| 187 | + Git allows "remote helpers" that have the form `<transport>::<address>`, |
| 188 | + one of these helpers (`ext::`) can be used to invoke any arbitrary command. |
| 189 | + See: |
| 190 | + - https://git-scm.com/docs/gitremote-helpers |
| 191 | + - https://git-scm.com/docs/git-remote-ext |
| 192 | + """ |
| 193 | + match = cls.re_unsafe_protocol.match(url) |
| 194 | + if match: |
| 195 | + protocol = match.group(1) |
| 196 | + raise UnsafeProtocolError( |
| 197 | + "The `" + protocol + "::` protocol looks suspicious, use `allow_unsafe_protocols=True` to allow it." |
| 198 | + ) |
| 199 | + |
| 200 | + @classmethod |
| 201 | + def check_unsafe_options(cls, options, unsafe_options): |
| 202 | + """ |
| 203 | + Check for unsafe options. |
| 204 | + Some options that are passed to `git <command>` can be used to execute |
| 205 | + arbitrary commands, this are blocked by default. |
| 206 | + """ |
| 207 | + # Options can be of the form `foo` or `--foo bar` `--foo=bar`, |
| 208 | + # so we need to check if they start with "--foo" or if they are equal to "foo". |
| 209 | + bare_unsafe_options = [ |
| 210 | + option.lstrip("-") |
| 211 | + for option in unsafe_options |
| 212 | + ] |
| 213 | + for option in options: |
| 214 | + for unsafe_option, bare_option in zip(unsafe_options, bare_unsafe_options): |
| 215 | + if option.startswith(unsafe_option) or option == bare_option: |
| 216 | + raise UnsafeOptionError( |
| 217 | + unsafe_option +" is not allowed, use `allow_unsafe_options=True` to allow it." |
| 218 | + ) |
| 219 | + |
178 | 220 | def __setstate__(self, d):
|
179 | 221 | dict_to_slots_and__excluded_are_none(self, d, excluded=self._excluded_)
|
180 | 222 |
|
@@ -1089,7 +1131,7 @@ def get_object_data(self, ref):
|
1089 | 1131 | :note: not threadsafe"""
|
1090 | 1132 | hexsha, typename, size, stream = self.stream_object_data(ref)
|
1091 | 1133 | data = stream.read(size)
|
1092 |
| - del(stream) |
| 1134 | + del (stream) |
1093 | 1135 | return (hexsha, typename, size, data)
|
1094 | 1136 |
|
1095 | 1137 | def stream_object_data(self, ref):
|
|
0 commit comments