Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit678a8fe

Browse files
authored
Merge pull requestgitpython-developers#1521 from stsewd/block-insecure-options
Block insecure options and protocols by default
2 parentsae6a6e4 +f4f2658 commit678a8fe

File tree

10 files changed

+752
-21
lines changed

10 files changed

+752
-21
lines changed

‎AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,5 @@ Contributors are:
5050
-Patrick Gerard
5151
-Luke Twist <itsluketwist@gmail.com>
5252
-Joseph Hale <me _at_ jhale.dev>
53+
-Santos Gallegos <stsewd _at_ proton.me>
5354
Portions derived from other open source works and are clearly marked.

‎git/cmd.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# This module is part of GitPython and is released under
55
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66
from __future__importannotations
7+
importre
78
fromcontextlibimportcontextmanager
89
importio
910
importlogging
@@ -24,7 +25,7 @@
2425
fromgit.excimportCommandError
2526
fromgit.utilimportis_cygwin_git,cygpath,expand_path,remove_password_if_present
2627

27-
from .excimportGitCommandError,GitCommandNotFound
28+
from .excimportGitCommandError,GitCommandNotFound,UnsafeOptionError,UnsafeProtocolError
2829
from .utilimport (
2930
LazyMixin,
3031
stream_copy,
@@ -262,6 +263,8 @@ class Git(LazyMixin):
262263

263264
_excluded_= ("cat_file_all","cat_file_header","_version_info")
264265

266+
re_unsafe_protocol=re.compile("(.+)::.+")
267+
265268
def__getstate__(self)->Dict[str,Any]:
266269
returnslots_to_dict(self,exclude=self._excluded_)
267270

@@ -454,6 +457,48 @@ def polish_url(cls, url: str, is_cygwin: Union[None, bool] = None) -> PathLike:
454457
url=url.replace("\\\\","\\").replace("\\","/")
455458
returnurl
456459

460+
@classmethod
461+
defcheck_unsafe_protocols(cls,url:str)->None:
462+
"""
463+
Check for unsafe protocols.
464+
465+
Apart from the usual protocols (http, git, ssh),
466+
Git allows "remote helpers" that have the form `<transport>::<address>`,
467+
one of these helpers (`ext::`) can be used to invoke any arbitrary command.
468+
469+
See:
470+
471+
- https://git-scm.com/docs/gitremote-helpers
472+
- https://git-scm.com/docs/git-remote-ext
473+
"""
474+
match=cls.re_unsafe_protocol.match(url)
475+
ifmatch:
476+
protocol=match.group(1)
477+
raiseUnsafeProtocolError(
478+
f"The `{protocol}::` protocol looks suspicious, use `allow_unsafe_protocols=True` to allow it."
479+
)
480+
481+
@classmethod
482+
defcheck_unsafe_options(cls,options:List[str],unsafe_options:List[str])->None:
483+
"""
484+
Check for unsafe options.
485+
486+
Some options that are passed to `git <command>` can be used to execute
487+
arbitrary commands, this are blocked by default.
488+
"""
489+
# Options can be of the form `foo` or `--foo bar` `--foo=bar`,
490+
# so we need to check if they start with "--foo" or if they are equal to "foo".
491+
bare_unsafe_options= [
492+
option.lstrip("-")
493+
foroptioninunsafe_options
494+
]
495+
foroptioninoptions:
496+
forunsafe_option,bare_optioninzip(unsafe_options,bare_unsafe_options):
497+
ifoption.startswith(unsafe_option)oroption==bare_option:
498+
raiseUnsafeOptionError(
499+
f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it."
500+
)
501+
457502
classAutoInterrupt(object):
458503
"""Kill/Interrupt the stored process instance once this instance goes out of scope. It is
459504
used to prevent processes piling up in case iterators stop reading.
@@ -1148,12 +1193,12 @@ def transform_kwargs(self, split_single_char_options: bool = True, **kwargs: Any
11481193
returnargs
11491194

11501195
@classmethod
1151-
def__unpack_args(cls,arg_list:Sequence[str])->List[str]:
1196+
def_unpack_args(cls,arg_list:Sequence[str])->List[str]:
11521197

11531198
outlist= []
11541199
ifisinstance(arg_list, (list,tuple)):
11551200
forarginarg_list:
1156-
outlist.extend(cls.__unpack_args(arg))
1201+
outlist.extend(cls._unpack_args(arg))
11571202
else:
11581203
outlist.append(str(arg_list))
11591204

@@ -1238,7 +1283,7 @@ def _call_process(
12381283
# Prepare the argument list
12391284

12401285
opt_args=self.transform_kwargs(**opts_kwargs)
1241-
ext_args=self.__unpack_args([aforainargsifaisnotNone])
1286+
ext_args=self._unpack_args([aforainargsifaisnotNone])
12421287

12431288
ifinsert_after_this_argisNone:
12441289
args_list=opt_args+ext_args

‎git/exc.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ class NoSuchPathError(GitError, OSError):
3737
"""Thrown if a path could not be access by the system."""
3838

3939

40+
classUnsafeProtocolError(GitError):
41+
"""Thrown if unsafe protocols are passed without being explicitly allowed."""
42+
43+
44+
classUnsafeOptionError(GitError):
45+
"""Thrown if unsafe options are passed without being explicitly allowed."""
46+
47+
4048
classCommandError(GitError):
4149
"""Base class for exceptions thrown at every stage of `Popen()` execution.
4250

‎git/objects/submodule/base.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,16 @@ def _module_abspath(cls, parent_repo: "Repo", path: PathLike, name: str) -> Path
272272
# end
273273

274274
@classmethod
275-
def_clone_repo(cls,repo:"Repo",url:str,path:PathLike,name:str,**kwargs:Any)->"Repo":
275+
def_clone_repo(
276+
cls,
277+
repo:"Repo",
278+
url:str,
279+
path:PathLike,
280+
name:str,
281+
allow_unsafe_options:bool=False,
282+
allow_unsafe_protocols:bool=False,
283+
**kwargs:Any,
284+
)->"Repo":
276285
""":return: Repo instance of newly cloned repository
277286
:param repo: our parent repository
278287
:param url: url to clone from
@@ -289,7 +298,13 @@ def _clone_repo(cls, repo: "Repo", url: str, path: PathLike, name: str, **kwargs
289298
module_checkout_path=osp.join(str(repo.working_tree_dir),path)
290299
# end
291300

292-
clone=git.Repo.clone_from(url,module_checkout_path,**kwargs)
301+
clone=git.Repo.clone_from(
302+
url,
303+
module_checkout_path,
304+
allow_unsafe_options=allow_unsafe_options,
305+
allow_unsafe_protocols=allow_unsafe_protocols,
306+
**kwargs,
307+
)
293308
ifcls._need_gitfile_submodules(repo.git):
294309
cls._write_git_file_and_module_config(module_checkout_path,module_abspath)
295310
# end
@@ -359,6 +374,8 @@ def add(
359374
depth:Union[int,None]=None,
360375
env:Union[Mapping[str,str],None]=None,
361376
clone_multi_options:Union[Sequence[TBD],None]=None,
377+
allow_unsafe_options:bool=False,
378+
allow_unsafe_protocols:bool=False,
362379
)->"Submodule":
363380
"""Add a new submodule to the given repository. This will alter the index
364381
as well as the .gitmodules file, but will not create a new commit.
@@ -475,7 +492,16 @@ def add(
475492
kwargs["multi_options"]=clone_multi_options
476493

477494
# _clone_repo(cls, repo, url, path, name, **kwargs):
478-
mrepo=cls._clone_repo(repo,url,path,name,env=env,**kwargs)
495+
mrepo=cls._clone_repo(
496+
repo,
497+
url,
498+
path,
499+
name,
500+
env=env,
501+
allow_unsafe_options=allow_unsafe_options,
502+
allow_unsafe_protocols=allow_unsafe_protocols,
503+
**kwargs,
504+
)
479505
# END verify url
480506

481507
## See #525 for ensuring git urls in config-files valid under Windows.
@@ -520,6 +546,8 @@ def update(
520546
keep_going:bool=False,
521547
env:Union[Mapping[str,str],None]=None,
522548
clone_multi_options:Union[Sequence[TBD],None]=None,
549+
allow_unsafe_options:bool=False,
550+
allow_unsafe_protocols:bool=False,
523551
)->"Submodule":
524552
"""Update the repository of this submodule to point to the checkout
525553
we point at with the binsha of this instance.
@@ -643,6 +671,8 @@ def update(
643671
n=True,
644672
env=env,
645673
multi_options=clone_multi_options,
674+
allow_unsafe_options=allow_unsafe_options,
675+
allow_unsafe_protocols=allow_unsafe_protocols,
646676
)
647677
# END handle dry-run
648678
progress.update(

‎git/remote.py

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,23 @@ class Remote(LazyMixin, IterableObj):
539539
__slots__= ("repo","name","_config_reader")
540540
_id_attribute_="name"
541541

542+
unsafe_git_fetch_options= [
543+
# This option allows users to execute arbitrary commands.
544+
# https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt---upload-packltupload-packgt
545+
"--upload-pack",
546+
]
547+
unsafe_git_pull_options= [
548+
# This option allows users to execute arbitrary commands.
549+
# https://git-scm.com/docs/git-pull#Documentation/git-pull.txt---upload-packltupload-packgt
550+
"--upload-pack"
551+
]
552+
unsafe_git_push_options= [
553+
# This option allows users to execute arbitrary commands.
554+
# https://git-scm.com/docs/git-push#Documentation/git-push.txt---execltgit-receive-packgt
555+
"--receive-pack",
556+
"--exec",
557+
]
558+
542559
def__init__(self,repo:"Repo",name:str)->None:
543560
"""Initialize a remote instance
544561
@@ -615,7 +632,9 @@ def iter_items(cls, repo: "Repo", *args: Any, **kwargs: Any) -> Iterator["Remote
615632
yieldRemote(repo,section[lbound+1 :rbound])
616633
# END for each configuration section
617634

618-
defset_url(self,new_url:str,old_url:Optional[str]=None,**kwargs:Any)->"Remote":
635+
defset_url(
636+
self,new_url:str,old_url:Optional[str]=None,allow_unsafe_protocols:bool=False,**kwargs:Any
637+
)->"Remote":
619638
"""Configure URLs on current remote (cf command git remote set_url)
620639
621640
This command manages URLs on the remote.
@@ -624,15 +643,17 @@ def set_url(self, new_url: str, old_url: Optional[str] = None, **kwargs: Any) ->
624643
:param old_url: when set, replaces this URL with new_url for the remote
625644
:return: self
626645
"""
646+
ifnotallow_unsafe_protocols:
647+
Git.check_unsafe_protocols(new_url)
627648
scmd="set-url"
628649
kwargs["insert_kwargs_after"]=scmd
629650
ifold_url:
630-
self.repo.git.remote(scmd,self.name,new_url,old_url,**kwargs)
651+
self.repo.git.remote(scmd,"--",self.name,new_url,old_url,**kwargs)
631652
else:
632-
self.repo.git.remote(scmd,self.name,new_url,**kwargs)
653+
self.repo.git.remote(scmd,"--",self.name,new_url,**kwargs)
633654
returnself
634655

635-
defadd_url(self,url:str,**kwargs:Any)->"Remote":
656+
defadd_url(self,url:str,allow_unsafe_protocols:bool=False,**kwargs:Any)->"Remote":
636657
"""Adds a new url on current remote (special case of git remote set_url)
637658
638659
This command adds new URLs to a given remote, making it possible to have
@@ -641,7 +662,7 @@ def add_url(self, url: str, **kwargs: Any) -> "Remote":
641662
:param url: string being the URL to add as an extra remote URL
642663
:return: self
643664
"""
644-
returnself.set_url(url,add=True)
665+
returnself.set_url(url,add=True,allow_unsafe_protocols=allow_unsafe_protocols)
645666

646667
defdelete_url(self,url:str,**kwargs:Any)->"Remote":
647668
"""Deletes a new url on current remote (special case of git remote set_url)
@@ -733,7 +754,7 @@ def stale_refs(self) -> IterableList[Reference]:
733754
returnout_refs
734755

735756
@classmethod
736-
defcreate(cls,repo:"Repo",name:str,url:str,**kwargs:Any)->"Remote":
757+
defcreate(cls,repo:"Repo",name:str,url:str,allow_unsafe_protocols:bool=False,**kwargs:Any)->"Remote":
737758
"""Create a new remote to the given repository
738759
:param repo: Repository instance that is to receive the new remote
739760
:param name: Desired name of the remote
@@ -743,7 +764,10 @@ def create(cls, repo: "Repo", name: str, url: str, **kwargs: Any) -> "Remote":
743764
:raise GitCommandError: in case an origin with that name already exists"""
744765
scmd="add"
745766
kwargs["insert_kwargs_after"]=scmd
746-
repo.git.remote(scmd,name,Git.polish_url(url),**kwargs)
767+
url=Git.polish_url(url)
768+
ifnotallow_unsafe_protocols:
769+
Git.check_unsafe_protocols(url)
770+
repo.git.remote(scmd,"--",name,url,**kwargs)
747771
returncls(repo,name)
748772

749773
# add is an alias
@@ -925,6 +949,8 @@ def fetch(
925949
progress:Union[RemoteProgress,None,"UpdateProgress"]=None,
926950
verbose:bool=True,
927951
kill_after_timeout:Union[None,float]=None,
952+
allow_unsafe_protocols:bool=False,
953+
allow_unsafe_options:bool=False,
928954
**kwargs:Any,
929955
)->IterableList[FetchInfo]:
930956
"""Fetch the latest changes for this remote
@@ -967,6 +993,14 @@ def fetch(
967993
else:
968994
args= [refspec]
969995

996+
ifnotallow_unsafe_protocols:
997+
forrefinargs:
998+
ifref:
999+
Git.check_unsafe_protocols(ref)
1000+
1001+
ifnotallow_unsafe_options:
1002+
Git.check_unsafe_options(options=list(kwargs.keys()),unsafe_options=self.unsafe_git_fetch_options)
1003+
9701004
proc=self.repo.git.fetch(
9711005
"--",self,*args,as_process=True,with_stdout=False,universal_newlines=True,v=verbose,**kwargs
9721006
)
@@ -980,6 +1014,8 @@ def pull(
9801014
refspec:Union[str,List[str],None]=None,
9811015
progress:Union[RemoteProgress,"UpdateProgress",None]=None,
9821016
kill_after_timeout:Union[None,float]=None,
1017+
allow_unsafe_protocols:bool=False,
1018+
allow_unsafe_options:bool=False,
9831019
**kwargs:Any,
9841020
)->IterableList[FetchInfo]:
9851021
"""Pull changes from the given branch, being the same as a fetch followed
@@ -994,6 +1030,15 @@ def pull(
9941030
# No argument refspec, then ensure the repo's config has a fetch refspec.
9951031
self._assert_refspec()
9961032
kwargs=add_progress(kwargs,self.repo.git,progress)
1033+
1034+
refspec=Git._unpack_args(refspecor [])
1035+
ifnotallow_unsafe_protocols:
1036+
forrefinrefspec:
1037+
Git.check_unsafe_protocols(ref)
1038+
1039+
ifnotallow_unsafe_options:
1040+
Git.check_unsafe_options(options=list(kwargs.keys()),unsafe_options=self.unsafe_git_pull_options)
1041+
9971042
proc=self.repo.git.pull(
9981043
"--",self,refspec,with_stdout=False,as_process=True,universal_newlines=True,v=True,**kwargs
9991044
)
@@ -1007,6 +1052,8 @@ def push(
10071052
refspec:Union[str,List[str],None]=None,
10081053
progress:Union[RemoteProgress,"UpdateProgress",Callable[...,RemoteProgress],None]=None,
10091054
kill_after_timeout:Union[None,float]=None,
1055+
allow_unsafe_protocols:bool=False,
1056+
allow_unsafe_options:bool=False,
10101057
**kwargs:Any,
10111058
)->PushInfoList:
10121059
"""Push changes from source branch in refspec to target branch in refspec.
@@ -1037,6 +1084,15 @@ def push(
10371084
be 0.
10381085
Call ``.raise_if_error()`` on the returned object to raise on any failure."""
10391086
kwargs=add_progress(kwargs,self.repo.git,progress)
1087+
1088+
refspec=Git._unpack_args(refspecor [])
1089+
ifnotallow_unsafe_protocols:
1090+
forrefinrefspec:
1091+
Git.check_unsafe_protocols(ref)
1092+
1093+
ifnotallow_unsafe_options:
1094+
Git.check_unsafe_options(options=list(kwargs.keys()),unsafe_options=self.unsafe_git_push_options)
1095+
10401096
proc=self.repo.git.push(
10411097
"--",
10421098
self,

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp