diff options
author | Narpat Mali <narpat.mali@windriver.com> | 2023-01-12 14:58:37 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-01-26 23:37:05 +0000 |
commit | 07213601fd865e698b4f7f6bd61d824e9d8181d2 (patch) | |
tree | 1babdcb8db163079a69026de690320cb36937568 | |
parent | fd36d262b86192bbc547f9a1e7aada5e94dccb8d (diff) | |
download | poky-07213601fd865e698b4f7f6bd61d824e9d8181d2.tar.gz |
python3-git: fix for CVE-2022-24439
All versions of package gitpython are vulnerable to Remote Code Execution
(RCE) due to improper user input validation, which makes it possible to
inject a maliciously crafted remote URL into the clone command. Exploiting
this vulnerability is possible because the library makes external calls to
git without sufficient sanitization of input arguments.
CVE: CVE-2022-24439
Upstream-Status: Backport
Reference:
https://github.com/gitpython-developers/GitPython/discussions/1529
https://github.com/gitpython-developers/GitPython/pull/1518
https://github.com/gitpython-developers/GitPython/pull/1521
(From OE-Core rev: 55f93e3786290dfa5ac72b5969bb2793f6a98bde)
Signed-off-by: Narpat Mali <narpat.mali@windriver.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
3 files changed, 589 insertions, 0 deletions
diff --git a/meta/recipes-devtools/python/python3-git/0001-python3-git-CVE-2022-24439-fix-from-PR-1518.patch b/meta/recipes-devtools/python/python3-git/0001-python3-git-CVE-2022-24439-fix-from-PR-1518.patch new file mode 100644 index 0000000000..16192b22c7 --- /dev/null +++ b/meta/recipes-devtools/python/python3-git/0001-python3-git-CVE-2022-24439-fix-from-PR-1518.patch | |||
@@ -0,0 +1,97 @@ | |||
1 | From 6ebe9231cd34dacd32a964859bc509aaa1e3f5fd Mon Sep 17 00:00:00 2001 | ||
2 | From: Narpat Mali <narpat.mali@windriver.com> | ||
3 | Date: Fri, 6 Jan 2023 14:13:10 +0000 | ||
4 | Subject: [PATCH] python3-git: CVE-2022-24439 fix from PR 1518 | ||
5 | |||
6 | Fix command injection | ||
7 | Add `--` in some commands that receive user input | ||
8 | and if interpreted as options could lead to remote | ||
9 | code execution (RCE). | ||
10 | |||
11 | There may be more commands that could benefit from `--` | ||
12 | so the input is never interpreted as an option, | ||
13 | but most of those aren't dangerous. | ||
14 | |||
15 | Fixed commands: | ||
16 | |||
17 | - push | ||
18 | - pull | ||
19 | - fetch | ||
20 | - clone/clone_from and friends | ||
21 | - archive (not sure if this one can be exploited, but it doesn't hurt | ||
22 | adding `--` :)) | ||
23 | |||
24 | For anyone using GitPython and exposing any of the GitPython methods to users, | ||
25 | make sure to always validate the input (like if starts with `--`). | ||
26 | And for anyone allowing users to pass arbitrary options, be aware | ||
27 | that some options may lead fo RCE, like `--exc`, `--upload-pack`, | ||
28 | `--receive-pack`, `--config` (#1516). | ||
29 | |||
30 | Ref #1517 | ||
31 | |||
32 | CVE: CVE-2022-24439 | ||
33 | |||
34 | Upstream-Status: Backport [https://github.com/gitpython-developers/GitPython/pull/1518] | ||
35 | |||
36 | Signed-off-by: Narpat Mali <narpat.mali@windriver.com> | ||
37 | --- | ||
38 | git/remote.py | 6 +++--- | ||
39 | git/repo/base.py | 4 ++-- | ||
40 | 2 files changed, 5 insertions(+), 5 deletions(-) | ||
41 | |||
42 | diff --git a/git/remote.py b/git/remote.py | ||
43 | index 56f3c5b..59681bc 100644 | ||
44 | --- a/git/remote.py | ||
45 | +++ b/git/remote.py | ||
46 | @@ -881,7 +881,7 @@ class Remote(LazyMixin, IterableObj): | ||
47 | else: | ||
48 | args = [refspec] | ||
49 | |||
50 | - proc = self.repo.git.fetch(self, *args, as_process=True, with_stdout=False, | ||
51 | + proc = self.repo.git.fetch("--", self, *args, as_process=True, with_stdout=False, | ||
52 | universal_newlines=True, v=verbose, **kwargs) | ||
53 | res = self._get_fetch_info_from_stderr(proc, progress, | ||
54 | kill_after_timeout=kill_after_timeout) | ||
55 | @@ -905,7 +905,7 @@ class Remote(LazyMixin, IterableObj): | ||
56 | # No argument refspec, then ensure the repo's config has a fetch refspec. | ||
57 | self._assert_refspec() | ||
58 | kwargs = add_progress(kwargs, self.repo.git, progress) | ||
59 | - proc = self.repo.git.pull(self, refspec, with_stdout=False, as_process=True, | ||
60 | + proc = self.repo.git.pull("--", self, refspec, with_stdout=False, as_process=True, | ||
61 | universal_newlines=True, v=True, **kwargs) | ||
62 | res = self._get_fetch_info_from_stderr(proc, progress, | ||
63 | kill_after_timeout=kill_after_timeout) | ||
64 | @@ -945,7 +945,7 @@ class Remote(LazyMixin, IterableObj): | ||
65 | If the operation fails completely, the length of the returned IterableList will | ||
66 | be 0.""" | ||
67 | kwargs = add_progress(kwargs, self.repo.git, progress) | ||
68 | - proc = self.repo.git.push(self, refspec, porcelain=True, as_process=True, | ||
69 | + proc = self.repo.git.push("--", self, refspec, porcelain=True, as_process=True, | ||
70 | universal_newlines=True, | ||
71 | kill_after_timeout=kill_after_timeout, | ||
72 | **kwargs) | ||
73 | diff --git a/git/repo/base.py b/git/repo/base.py | ||
74 | index 7713c91..f14f929 100644 | ||
75 | --- a/git/repo/base.py | ||
76 | +++ b/git/repo/base.py | ||
77 | @@ -1072,7 +1072,7 @@ class Repo(object): | ||
78 | multi = None | ||
79 | if multi_options: | ||
80 | multi = shlex.split(' '.join(multi_options)) | ||
81 | - proc = git.clone(multi, Git.polish_url(str(url)), clone_path, with_extended_output=True, as_process=True, | ||
82 | + proc = git.clone("--", multi, Git.polish_url(str(url)), clone_path, with_extended_output=True, as_process=True, | ||
83 | v=True, universal_newlines=True, **add_progress(kwargs, git, progress)) | ||
84 | if progress: | ||
85 | handle_process_output(proc, None, to_progress_instance(progress).new_message_handler(), | ||
86 | @@ -1173,7 +1173,7 @@ class Repo(object): | ||
87 | if not isinstance(path, (tuple, list)): | ||
88 | path = [path] | ||
89 | # end assure paths is list | ||
90 | - self.git.archive(treeish, *path, **kwargs) | ||
91 | + self.git.archive("--", treeish, *path, **kwargs) | ||
92 | return self | ||
93 | |||
94 | def has_separate_working_tree(self) -> bool: | ||
95 | -- | ||
96 | 2.34.1 | ||
97 | |||
diff --git a/meta/recipes-devtools/python/python3-git/0001-python3-git-CVE-2022-24439-fix-from-PR-1521.patch b/meta/recipes-devtools/python/python3-git/0001-python3-git-CVE-2022-24439-fix-from-PR-1521.patch new file mode 100644 index 0000000000..e3e66ec450 --- /dev/null +++ b/meta/recipes-devtools/python/python3-git/0001-python3-git-CVE-2022-24439-fix-from-PR-1521.patch | |||
@@ -0,0 +1,488 @@ | |||
1 | From fe9b71628767610a238e47cd46b82d411a7e871a Mon Sep 17 00:00:00 2001 | ||
2 | From: Narpat Mali <narpat.mali@windriver.com> | ||
3 | Date: Sat, 7 Jan 2023 17:16:57 +0000 | ||
4 | Subject: [PATCH] python3-git: CVE-2022-24439 fix from PR 1521 | ||
5 | |||
6 | Forbid unsafe protocol URLs in Repo.clone{,_from}() | ||
7 | Since the URL is passed directly to git clone, and the remote-ext helper | ||
8 | will happily execute shell commands, so by default disallow URLs that | ||
9 | contain a "::" unless a new unsafe_protocols kwarg is passed. | ||
10 | (CVE-2022-24439) | ||
11 | |||
12 | Fixes #1515 | ||
13 | |||
14 | CVE: CVE-2022-24439 | ||
15 | |||
16 | Upstream-Status: Backport [https://github.com/gitpython-developers/GitPython/pull/1521] | ||
17 | |||
18 | Signed-off-by: Narpat Mali <narpat.mali@windriver.com> | ||
19 | --- | ||
20 | git/cmd.py | 51 ++++++++++++++++++++++++-- | ||
21 | git/exc.py | 8 ++++ | ||
22 | git/objects/submodule/base.py | 19 ++++++---- | ||
23 | git/remote.py | 69 +++++++++++++++++++++++++++++++---- | ||
24 | git/repo/base.py | 44 ++++++++++++++++++---- | ||
25 | 5 files changed, 166 insertions(+), 25 deletions(-) | ||
26 | |||
27 | diff --git a/git/cmd.py b/git/cmd.py | ||
28 | index 4f05698..77026d6 100644 | ||
29 | --- a/git/cmd.py | ||
30 | +++ b/git/cmd.py | ||
31 | @@ -4,6 +4,7 @@ | ||
32 | # This module is part of GitPython and is released under | ||
33 | # the BSD License: http://www.opensource.org/licenses/bsd-license.php | ||
34 | from __future__ import annotations | ||
35 | +import re | ||
36 | from contextlib import contextmanager | ||
37 | import io | ||
38 | import logging | ||
39 | @@ -31,7 +32,9 @@ from git.util import is_cygwin_git, cygpath, expand_path, remove_password_if_pre | ||
40 | |||
41 | from .exc import ( | ||
42 | GitCommandError, | ||
43 | - GitCommandNotFound | ||
44 | + GitCommandNotFound, | ||
45 | + UnsafeOptionError, | ||
46 | + UnsafeProtocolError | ||
47 | ) | ||
48 | from .util import ( | ||
49 | LazyMixin, | ||
50 | @@ -225,6 +228,8 @@ class Git(LazyMixin): | ||
51 | |||
52 | _excluded_ = ('cat_file_all', 'cat_file_header', '_version_info') | ||
53 | |||
54 | + re_unsafe_protocol = re.compile("(.+)::.+") | ||
55 | + | ||
56 | def __getstate__(self) -> Dict[str, Any]: | ||
57 | return slots_to_dict(self, exclude=self._excluded_) | ||
58 | |||
59 | @@ -400,6 +405,44 @@ class Git(LazyMixin): | ||
60 | url = url.replace("\\\\", "\\").replace("\\", "/") | ||
61 | return url | ||
62 | |||
63 | + @classmethod | ||
64 | + def check_unsafe_protocols(cls, url: str) -> None: | ||
65 | + """ | ||
66 | + Check for unsafe protocols. | ||
67 | + Apart from the usual protocols (http, git, ssh), | ||
68 | + Git allows "remote helpers" that have the form `<transport>::<address>`, | ||
69 | + one of these helpers (`ext::`) can be used to invoke any arbitrary command. | ||
70 | + See: | ||
71 | + - https://git-scm.com/docs/gitremote-helpers | ||
72 | + - https://git-scm.com/docs/git-remote-ext | ||
73 | + """ | ||
74 | + match = cls.re_unsafe_protocol.match(url) | ||
75 | + if match: | ||
76 | + protocol = match.group(1) | ||
77 | + raise UnsafeProtocolError( | ||
78 | + f"The `{protocol}::` protocol looks suspicious, use `allow_unsafe_protocols=True` to allow it." | ||
79 | + ) | ||
80 | + | ||
81 | + @classmethod | ||
82 | + def check_unsafe_options(cls, options: List[str], unsafe_options: List[str]) -> None: | ||
83 | + """ | ||
84 | + Check for unsafe options. | ||
85 | + Some options that are passed to `git <command>` can be used to execute | ||
86 | + arbitrary commands, this are blocked by default. | ||
87 | + """ | ||
88 | + # Options can be of the form `foo` or `--foo bar` `--foo=bar`, | ||
89 | + # so we need to check if they start with "--foo" or if they are equal to "foo". | ||
90 | + bare_unsafe_options = [ | ||
91 | + option.lstrip("-") | ||
92 | + for option in unsafe_options | ||
93 | + ] | ||
94 | + for option in options: | ||
95 | + for unsafe_option, bare_option in zip(unsafe_options, bare_unsafe_options): | ||
96 | + if option.startswith(unsafe_option) or option == bare_option: | ||
97 | + raise UnsafeOptionError( | ||
98 | + f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it." | ||
99 | + ) | ||
100 | + | ||
101 | class AutoInterrupt(object): | ||
102 | """Kill/Interrupt the stored process instance once this instance goes out of scope. It is | ||
103 | used to prevent processes piling up in case iterators stop reading. | ||
104 | @@ -1068,12 +1111,12 @@ class Git(LazyMixin): | ||
105 | return args | ||
106 | |||
107 | @classmethod | ||
108 | - def __unpack_args(cls, arg_list: Sequence[str]) -> List[str]: | ||
109 | + def _unpack_args(cls, arg_list: Sequence[str]) -> List[str]: | ||
110 | |||
111 | outlist = [] | ||
112 | if isinstance(arg_list, (list, tuple)): | ||
113 | for arg in arg_list: | ||
114 | - outlist.extend(cls.__unpack_args(arg)) | ||
115 | + outlist.extend(cls._unpack_args(arg)) | ||
116 | else: | ||
117 | outlist.append(str(arg_list)) | ||
118 | |||
119 | @@ -1154,7 +1197,7 @@ class Git(LazyMixin): | ||
120 | # Prepare the argument list | ||
121 | |||
122 | opt_args = self.transform_kwargs(**opts_kwargs) | ||
123 | - ext_args = self.__unpack_args([a for a in args if a is not None]) | ||
124 | + ext_args = self._unpack_args([a for a in args if a is not None]) | ||
125 | |||
126 | if insert_after_this_arg is None: | ||
127 | args_list = opt_args + ext_args | ||
128 | diff --git a/git/exc.py b/git/exc.py | ||
129 | index e8ff784..5c96db2 100644 | ||
130 | --- a/git/exc.py | ||
131 | +++ b/git/exc.py | ||
132 | @@ -36,6 +36,14 @@ class NoSuchPathError(GitError, OSError): | ||
133 | """ Thrown if a path could not be access by the system. """ | ||
134 | |||
135 | |||
136 | +class UnsafeProtocolError(GitError): | ||
137 | + """Thrown if unsafe protocols are passed without being explicitly allowed.""" | ||
138 | + | ||
139 | + | ||
140 | +class UnsafeOptionError(GitError): | ||
141 | + """Thrown if unsafe options are passed without being explicitly allowed.""" | ||
142 | + | ||
143 | + | ||
144 | class CommandError(GitError): | ||
145 | """Base class for exceptions thrown at every stage of `Popen()` execution. | ||
146 | |||
147 | diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py | ||
148 | index f782045..deb224e 100644 | ||
149 | --- a/git/objects/submodule/base.py | ||
150 | +++ b/git/objects/submodule/base.py | ||
151 | @@ -264,7 +264,8 @@ class Submodule(IndexObject, TraversableIterableObj): | ||
152 | # end | ||
153 | |||
154 | @classmethod | ||
155 | - def _clone_repo(cls, repo: 'Repo', url: str, path: PathLike, name: str, **kwargs: Any) -> 'Repo': | ||
156 | + def _clone_repo(cls, repo: 'Repo', url: str, path: PathLike, name: str, | ||
157 | + allow_unsafe_options: bool = False, allow_unsafe_protocols: bool = False,**kwargs: Any) -> 'Repo': | ||
158 | """:return: Repo instance of newly cloned repository | ||
159 | :param repo: our parent repository | ||
160 | :param url: url to clone from | ||
161 | @@ -281,7 +282,8 @@ class Submodule(IndexObject, TraversableIterableObj): | ||
162 | module_checkout_path = osp.join(str(repo.working_tree_dir), path) | ||
163 | # end | ||
164 | |||
165 | - clone = git.Repo.clone_from(url, module_checkout_path, **kwargs) | ||
166 | + clone = git.Repo.clone_from(url, module_checkout_path, allow_unsafe_options=allow_unsafe_options, | ||
167 | + allow_unsafe_protocols=allow_unsafe_protocols, **kwargs) | ||
168 | if cls._need_gitfile_submodules(repo.git): | ||
169 | cls._write_git_file_and_module_config(module_checkout_path, module_abspath) | ||
170 | # end | ||
171 | @@ -338,8 +340,8 @@ class Submodule(IndexObject, TraversableIterableObj): | ||
172 | @classmethod | ||
173 | def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = None, | ||
174 | branch: Union[str, None] = None, no_checkout: bool = False, depth: Union[int, None] = None, | ||
175 | - env: Union[Mapping[str, str], None] = None, clone_multi_options: Union[Sequence[TBD], None] = None | ||
176 | - ) -> 'Submodule': | ||
177 | + env: Union[Mapping[str, str], None] = None, clone_multi_options: Union[Sequence[TBD], None] = None, | ||
178 | + allow_unsafe_options: bool = False, allow_unsafe_protocols: bool = False,) -> 'Submodule': | ||
179 | """Add a new submodule to the given repository. This will alter the index | ||
180 | as well as the .gitmodules file, but will not create a new commit. | ||
181 | If the submodule already exists, no matter if the configuration differs | ||
182 | @@ -447,7 +449,8 @@ class Submodule(IndexObject, TraversableIterableObj): | ||
183 | kwargs['multi_options'] = clone_multi_options | ||
184 | |||
185 | # _clone_repo(cls, repo, url, path, name, **kwargs): | ||
186 | - mrepo = cls._clone_repo(repo, url, path, name, env=env, **kwargs) | ||
187 | + mrepo = cls._clone_repo(repo, url, path, name, env=env, allow_unsafe_options=allow_unsafe_options, | ||
188 | + allow_unsafe_protocols=allow_unsafe_protocols, **kwargs) | ||
189 | # END verify url | ||
190 | |||
191 | ## See #525 for ensuring git urls in config-files valid under Windows. | ||
192 | @@ -484,7 +487,8 @@ class Submodule(IndexObject, TraversableIterableObj): | ||
193 | def update(self, recursive: bool = False, init: bool = True, to_latest_revision: bool = False, | ||
194 | progress: Union['UpdateProgress', None] = None, dry_run: bool = False, | ||
195 | force: bool = False, keep_going: bool = False, env: Union[Mapping[str, str], None] = None, | ||
196 | - clone_multi_options: Union[Sequence[TBD], None] = None) -> 'Submodule': | ||
197 | + clone_multi_options: Union[Sequence[TBD], None] = None, allow_unsafe_options: bool = False, | ||
198 | + allow_unsafe_protocols: bool = False) -> 'Submodule': | ||
199 | """Update the repository of this submodule to point to the checkout | ||
200 | we point at with the binsha of this instance. | ||
201 | |||
202 | @@ -585,7 +589,8 @@ class Submodule(IndexObject, TraversableIterableObj): | ||
203 | (self.url, checkout_module_abspath, self.name)) | ||
204 | if not dry_run: | ||
205 | mrepo = self._clone_repo(self.repo, self.url, self.path, self.name, n=True, env=env, | ||
206 | - multi_options=clone_multi_options) | ||
207 | + multi_options=clone_multi_options, allow_unsafe_options=allow_unsafe_options, | ||
208 | + allow_unsafe_protocols=allow_unsafe_protocols) | ||
209 | # END handle dry-run | ||
210 | progress.update(END | CLONE, 0, 1, prefix + "Done cloning to %s" % checkout_module_abspath) | ||
211 | |||
212 | diff --git a/git/remote.py b/git/remote.py | ||
213 | index 59681bc..cea6b99 100644 | ||
214 | --- a/git/remote.py | ||
215 | +++ b/git/remote.py | ||
216 | @@ -473,6 +473,23 @@ class Remote(LazyMixin, IterableObj): | ||
217 | __slots__ = ("repo", "name", "_config_reader") | ||
218 | _id_attribute_ = "name" | ||
219 | |||
220 | + unsafe_git_fetch_options = [ | ||
221 | + # This option allows users to execute arbitrary commands. | ||
222 | + # https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt---upload-packltupload-packgt | ||
223 | + "--upload-pack", | ||
224 | + ] | ||
225 | + unsafe_git_pull_options = [ | ||
226 | + # This option allows users to execute arbitrary commands. | ||
227 | + # https://git-scm.com/docs/git-pull#Documentation/git-pull.txt---upload-packltupload-packgt | ||
228 | + "--upload-pack" | ||
229 | + ] | ||
230 | + unsafe_git_push_options = [ | ||
231 | + # This option allows users to execute arbitrary commands. | ||
232 | + # https://git-scm.com/docs/git-push#Documentation/git-push.txt---execltgit-receive-packgt | ||
233 | + "--receive-pack", | ||
234 | + "--exec", | ||
235 | + ] | ||
236 | + | ||
237 | def __init__(self, repo: 'Repo', name: str) -> None: | ||
238 | """Initialize a remote instance | ||
239 | |||
240 | @@ -549,7 +566,8 @@ class Remote(LazyMixin, IterableObj): | ||
241 | yield Remote(repo, section[lbound + 1:rbound]) | ||
242 | # END for each configuration section | ||
243 | |||
244 | - def set_url(self, new_url: str, old_url: Optional[str] = None, **kwargs: Any) -> 'Remote': | ||
245 | + def set_url(self, new_url: str, old_url: Optional[str] = None, | ||
246 | + allow_unsafe_protocols: bool = False, **kwargs: Any) -> 'Remote': | ||
247 | """Configure URLs on current remote (cf command git remote set_url) | ||
248 | |||
249 | This command manages URLs on the remote. | ||
250 | @@ -558,15 +576,17 @@ class Remote(LazyMixin, IterableObj): | ||
251 | :param old_url: when set, replaces this URL with new_url for the remote | ||
252 | :return: self | ||
253 | """ | ||
254 | + if not allow_unsafe_protocols: | ||
255 | + Git.check_unsafe_protocols(new_url) | ||
256 | scmd = 'set-url' | ||
257 | kwargs['insert_kwargs_after'] = scmd | ||
258 | if old_url: | ||
259 | - self.repo.git.remote(scmd, self.name, new_url, old_url, **kwargs) | ||
260 | + self.repo.git.remote(scmd, "--", self.name, new_url, old_url, **kwargs) | ||
261 | else: | ||
262 | - self.repo.git.remote(scmd, self.name, new_url, **kwargs) | ||
263 | + self.repo.git.remote(scmd, "--", self.name, new_url, **kwargs) | ||
264 | return self | ||
265 | |||
266 | - def add_url(self, url: str, **kwargs: Any) -> 'Remote': | ||
267 | + def add_url(self, url: str, allow_unsafe_protocols: bool = False, **kwargs: Any) -> 'Remote': | ||
268 | """Adds a new url on current remote (special case of git remote set_url) | ||
269 | |||
270 | This command adds new URLs to a given remote, making it possible to have | ||
271 | @@ -575,7 +595,7 @@ class Remote(LazyMixin, IterableObj): | ||
272 | :param url: string being the URL to add as an extra remote URL | ||
273 | :return: self | ||
274 | """ | ||
275 | - return self.set_url(url, add=True) | ||
276 | + return self.set_url(url, add=True, allow_unsafe_protocols=allow_unsafe_protocols) | ||
277 | |||
278 | def delete_url(self, url: str, **kwargs: Any) -> 'Remote': | ||
279 | """Deletes a new url on current remote (special case of git remote set_url) | ||
280 | @@ -667,7 +687,7 @@ class Remote(LazyMixin, IterableObj): | ||
281 | return out_refs | ||
282 | |||
283 | @ classmethod | ||
284 | - def create(cls, repo: 'Repo', name: str, url: str, **kwargs: Any) -> 'Remote': | ||
285 | + def create(cls, repo: 'Repo', name: str, url: str, allow_unsafe_protocols: bool = False, *kwargs: Any) -> 'Remote': | ||
286 | """Create a new remote to the given repository | ||
287 | :param repo: Repository instance that is to receive the new remote | ||
288 | :param name: Desired name of the remote | ||
289 | @@ -677,7 +697,10 @@ class Remote(LazyMixin, IterableObj): | ||
290 | :raise GitCommandError: in case an origin with that name already exists""" | ||
291 | scmd = 'add' | ||
292 | kwargs['insert_kwargs_after'] = scmd | ||
293 | - repo.git.remote(scmd, name, Git.polish_url(url), **kwargs) | ||
294 | + url = Git.polish_url(url) | ||
295 | + if not allow_unsafe_protocols: | ||
296 | + Git.check_unsafe_protocols(url) | ||
297 | + repo.git.remote(scmd, "--", name, url, **kwargs) | ||
298 | return cls(repo, name) | ||
299 | |||
300 | # add is an alias | ||
301 | @@ -840,6 +863,8 @@ class Remote(LazyMixin, IterableObj): | ||
302 | progress: Union[RemoteProgress, None, 'UpdateProgress'] = None, | ||
303 | verbose: bool = True, | ||
304 | kill_after_timeout: Union[None, float] = None, | ||
305 | + allow_unsafe_protocols: bool = False, | ||
306 | + allow_unsafe_options: bool = False, | ||
307 | **kwargs: Any) -> IterableList[FetchInfo]: | ||
308 | """Fetch the latest changes for this remote | ||
309 | |||
310 | @@ -881,6 +906,14 @@ class Remote(LazyMixin, IterableObj): | ||
311 | else: | ||
312 | args = [refspec] | ||
313 | |||
314 | + if not allow_unsafe_protocols: | ||
315 | + for ref in args: | ||
316 | + if ref: | ||
317 | + Git.check_unsafe_protocols(ref) | ||
318 | + | ||
319 | + if not allow_unsafe_options: | ||
320 | + Git.check_unsafe_options(options=list(kwargs.keys()), unsafe_options=self.unsafe_git_fetch_options) | ||
321 | + | ||
322 | proc = self.repo.git.fetch("--", self, *args, as_process=True, with_stdout=False, | ||
323 | universal_newlines=True, v=verbose, **kwargs) | ||
324 | res = self._get_fetch_info_from_stderr(proc, progress, | ||
325 | @@ -892,6 +925,8 @@ class Remote(LazyMixin, IterableObj): | ||
326 | def pull(self, refspec: Union[str, List[str], None] = None, | ||
327 | progress: Union[RemoteProgress, 'UpdateProgress', None] = None, | ||
328 | kill_after_timeout: Union[None, float] = None, | ||
329 | + allow_unsafe_protocols: bool = False, | ||
330 | + allow_unsafe_options: bool = False, | ||
331 | **kwargs: Any) -> IterableList[FetchInfo]: | ||
332 | """Pull changes from the given branch, being the same as a fetch followed | ||
333 | by a merge of branch with your local branch. | ||
334 | @@ -905,6 +940,15 @@ class Remote(LazyMixin, IterableObj): | ||
335 | # No argument refspec, then ensure the repo's config has a fetch refspec. | ||
336 | self._assert_refspec() | ||
337 | kwargs = add_progress(kwargs, self.repo.git, progress) | ||
338 | + | ||
339 | + refspec = Git._unpack_args(refspec or []) | ||
340 | + if not allow_unsafe_protocols: | ||
341 | + for ref in refspec: | ||
342 | + Git.check_unsafe_protocols(ref) | ||
343 | + | ||
344 | + if not allow_unsafe_options: | ||
345 | + Git.check_unsafe_options(options=list(kwargs.keys()), unsafe_options=self.unsafe_git_pull_options) | ||
346 | + | ||
347 | proc = self.repo.git.pull("--", self, refspec, with_stdout=False, as_process=True, | ||
348 | universal_newlines=True, v=True, **kwargs) | ||
349 | res = self._get_fetch_info_from_stderr(proc, progress, | ||
350 | @@ -916,6 +960,8 @@ class Remote(LazyMixin, IterableObj): | ||
351 | def push(self, refspec: Union[str, List[str], None] = None, | ||
352 | progress: Union[RemoteProgress, 'UpdateProgress', Callable[..., RemoteProgress], None] = None, | ||
353 | kill_after_timeout: Union[None, float] = None, | ||
354 | + allow_unsafe_protocols: bool = False, | ||
355 | + allow_unsafe_options: bool = False, | ||
356 | **kwargs: Any) -> IterableList[PushInfo]: | ||
357 | """Push changes from source branch in refspec to target branch in refspec. | ||
358 | |||
359 | @@ -945,6 +991,15 @@ class Remote(LazyMixin, IterableObj): | ||
360 | If the operation fails completely, the length of the returned IterableList will | ||
361 | be 0.""" | ||
362 | kwargs = add_progress(kwargs, self.repo.git, progress) | ||
363 | + | ||
364 | + refspec = Git._unpack_args(refspec or []) | ||
365 | + if not allow_unsafe_protocols: | ||
366 | + for ref in refspec: | ||
367 | + Git.check_unsafe_protocols(ref) | ||
368 | + | ||
369 | + if not allow_unsafe_options: | ||
370 | + Git.check_unsafe_options(options=list(kwargs.keys()), unsafe_options=self.unsafe_git_push_options) | ||
371 | + | ||
372 | proc = self.repo.git.push("--", self, refspec, porcelain=True, as_process=True, | ||
373 | universal_newlines=True, | ||
374 | kill_after_timeout=kill_after_timeout, | ||
375 | diff --git a/git/repo/base.py b/git/repo/base.py | ||
376 | index f14f929..7b3565b 100644 | ||
377 | --- a/git/repo/base.py | ||
378 | +++ b/git/repo/base.py | ||
379 | @@ -24,7 +24,11 @@ from git.compat import ( | ||
380 | ) | ||
381 | from git.config import GitConfigParser | ||
382 | from git.db import GitCmdObjectDB | ||
383 | -from git.exc import InvalidGitRepositoryError, NoSuchPathError, GitCommandError | ||
384 | +from git.exc import ( | ||
385 | + GitCommandError, | ||
386 | + InvalidGitRepositoryError, | ||
387 | + NoSuchPathError, | ||
388 | +) | ||
389 | from git.index import IndexFile | ||
390 | from git.objects import Submodule, RootModule, Commit | ||
391 | from git.refs import HEAD, Head, Reference, TagReference | ||
392 | @@ -97,6 +101,18 @@ class Repo(object): | ||
393 | re_author_committer_start = re.compile(r'^(author|committer)') | ||
394 | re_tab_full_line = re.compile(r'^\t(.*)$') | ||
395 | |||
396 | + unsafe_git_clone_options = [ | ||
397 | + # This option allows users to execute arbitrary commands. | ||
398 | + # https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---upload-packltupload-packgt | ||
399 | + "--upload-pack", | ||
400 | + "-u", | ||
401 | + # Users can override configuration variables | ||
402 | + # like `protocol.allow` or `core.gitProxy` to execute arbitrary commands. | ||
403 | + # https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---configltkeygtltvaluegt | ||
404 | + "--config", | ||
405 | + "-c", | ||
406 | + ] | ||
407 | + | ||
408 | # invariants | ||
409 | # represents the configuration level of a configuration file | ||
410 | config_level: ConfigLevels_Tup = ("system", "user", "global", "repository") | ||
411 | @@ -1049,7 +1065,8 @@ class Repo(object): | ||
412 | @ classmethod | ||
413 | def _clone(cls, git: 'Git', url: PathLike, path: PathLike, odb_default_type: Type[GitCmdObjectDB], | ||
414 | progress: Union['RemoteProgress', 'UpdateProgress', Callable[..., 'RemoteProgress'], None] = None, | ||
415 | - multi_options: Optional[List[str]] = None, **kwargs: Any | ||
416 | + multi_options: Optional[List[str]] = None, allow_unsafe_protocols: bool = False, | ||
417 | + allow_unsafe_options: bool = False, **kwargs: Any | ||
418 | ) -> 'Repo': | ||
419 | odbt = kwargs.pop('odbt', odb_default_type) | ||
420 | |||
421 | @@ -1072,6 +1089,12 @@ class Repo(object): | ||
422 | multi = None | ||
423 | if multi_options: | ||
424 | multi = shlex.split(' '.join(multi_options)) | ||
425 | + | ||
426 | + if not allow_unsafe_protocols: | ||
427 | + Git.check_unsafe_protocols(str(url)) | ||
428 | + if not allow_unsafe_options and multi_options: | ||
429 | + Git.check_unsafe_options(options=multi_options, unsafe_options=cls.unsafe_git_clone_options) | ||
430 | + | ||
431 | proc = git.clone("--", multi, Git.polish_url(str(url)), clone_path, with_extended_output=True, as_process=True, | ||
432 | v=True, universal_newlines=True, **add_progress(kwargs, git, progress)) | ||
433 | if progress: | ||
434 | @@ -1107,7 +1130,9 @@ class Repo(object): | ||
435 | return repo | ||
436 | |||
437 | def clone(self, path: PathLike, progress: Optional[Callable] = None, | ||
438 | - multi_options: Optional[List[str]] = None, **kwargs: Any) -> 'Repo': | ||
439 | + multi_options: Optional[List[str]] = None, unsafe_protocols: bool = False, | ||
440 | + allow_unsafe_protocols: bool = False, allow_unsafe_options: bool = False, | ||
441 | + **kwargs: Any) -> 'Repo': | ||
442 | """Create a clone from this repository. | ||
443 | |||
444 | :param path: is the full path of the new repo (traditionally ends with ./<name>.git). | ||
445 | @@ -1116,18 +1141,21 @@ class Repo(object): | ||
446 | option per list item which is passed exactly as specified to clone. | ||
447 | For example ['--config core.filemode=false', '--config core.ignorecase', | ||
448 | '--recurse-submodule=repo1_path', '--recurse-submodule=repo2_path'] | ||
449 | + :param unsafe_protocols: Allow unsafe protocols to be used, like ex | ||
450 | :param kwargs: | ||
451 | * odbt = ObjectDatabase Type, allowing to determine the object database | ||
452 | implementation used by the returned Repo instance | ||
453 | * All remaining keyword arguments are given to the git-clone command | ||
454 | |||
455 | :return: ``git.Repo`` (the newly cloned repo)""" | ||
456 | - return self._clone(self.git, self.common_dir, path, type(self.odb), progress, multi_options, **kwargs) | ||
457 | + return self._clone(self.git, self.common_dir, path, type(self.odb), progress, multi_options, | ||
458 | + allow_unsafe_protocols=allow_unsafe_protocols, allow_unsafe_options=allow_unsafe_options, **kwargs) | ||
459 | |||
460 | @ classmethod | ||
461 | def clone_from(cls, url: PathLike, to_path: PathLike, progress: Optional[Callable] = None, | ||
462 | - env: Optional[Mapping[str, str]] = None, | ||
463 | - multi_options: Optional[List[str]] = None, **kwargs: Any) -> 'Repo': | ||
464 | + env: Optional[Mapping[str, str]] = None, multi_options: Optional[List[str]] = None, | ||
465 | + unsafe_protocols: bool = False, allow_unsafe_protocols: bool = False, | ||
466 | + allow_unsafe_options: bool = False, **kwargs: Any) -> 'Repo': | ||
467 | """Create a clone from the given URL | ||
468 | |||
469 | :param url: valid git url, see http://www.kernel.org/pub/software/scm/git/docs/git-clone.html#URLS | ||
470 | @@ -1140,12 +1168,14 @@ class Repo(object): | ||
471 | If you want to unset some variable, consider providing empty string | ||
472 | as its value. | ||
473 | :param multi_options: See ``clone`` method | ||
474 | + :param unsafe_protocols: Allow unsafe protocols to be used, like ext | ||
475 | :param kwargs: see the ``clone`` method | ||
476 | :return: Repo instance pointing to the cloned directory""" | ||
477 | git = cls.GitCommandWrapperType(os.getcwd()) | ||
478 | if env is not None: | ||
479 | git.update_environment(**env) | ||
480 | - return cls._clone(git, url, to_path, GitCmdObjectDB, progress, multi_options, **kwargs) | ||
481 | + return cls._clone(git, url, to_path, GitCmdObjectDB, progress, multi_options, | ||
482 | + allow_unsafe_protocols=allow_unsafe_protocols, allow_unsafe_options=allow_unsafe_options, **kwargs) | ||
483 | |||
484 | def archive(self, ostream: Union[TextIO, BinaryIO], treeish: Optional[str] = None, | ||
485 | prefix: Optional[str] = None, **kwargs: Any) -> Repo: | ||
486 | -- | ||
487 | 2.34.1 | ||
488 | |||
diff --git a/meta/recipes-devtools/python/python3-git_3.1.27.bb b/meta/recipes-devtools/python/python3-git_3.1.27.bb index fb1bae8f8e..1bd1426926 100644 --- a/meta/recipes-devtools/python/python3-git_3.1.27.bb +++ b/meta/recipes-devtools/python/python3-git_3.1.27.bb | |||
@@ -12,6 +12,10 @@ PYPI_PACKAGE = "GitPython" | |||
12 | 12 | ||
13 | inherit pypi python_setuptools_build_meta | 13 | inherit pypi python_setuptools_build_meta |
14 | 14 | ||
15 | SRC_URI += "file://0001-python3-git-CVE-2022-24439-fix-from-PR-1518.patch \ | ||
16 | file://0001-python3-git-CVE-2022-24439-fix-from-PR-1521.patch \ | ||
17 | " | ||
18 | |||
15 | SRC_URI[sha256sum] = "1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704" | 19 | SRC_URI[sha256sum] = "1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704" |
16 | 20 | ||
17 | DEPENDS += " ${PYTHON_PN}-gitdb" | 21 | DEPENDS += " ${PYTHON_PN}-gitdb" |