Block unsafe options and protocols by default · gitpython-developers/GitPython@e6108c7
@@ -535,6 +535,23 @@ class Remote(LazyMixin, IterableObj):
535535__slots__ = ("repo", "name", "_config_reader")
536536_id_attribute_ = "name"
537537538+unsafe_git_fetch_options = [
539+# This option allows users to execute arbitrary commands.
540+# https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt---upload-packltupload-packgt
541+"--upload-pack",
542+ ]
543+unsafe_git_pull_options = [
544+# This option allows users to execute arbitrary commands.
545+# https://git-scm.com/docs/git-pull#Documentation/git-pull.txt---upload-packltupload-packgt
546+"--upload-pack"
547+ ]
548+unsafe_git_push_options = [
549+# This option allows users to execute arbitrary commands.
550+# https://git-scm.com/docs/git-push#Documentation/git-push.txt---execltgit-receive-packgt
551+"--receive-pack",
552+"--exec",
553+ ]
554+538555def __init__(self, repo: "Repo", name: str) -> None:
539556"""Initialize a remote instance
540557@@ -611,7 +628,9 @@ def iter_items(cls, repo: "Repo", *args: Any, **kwargs: Any) -> Iterator["Remote
611628yield Remote(repo, section[lbound + 1 : rbound])
612629# END for each configuration section
613630614-def set_url(self, new_url: str, old_url: Optional[str] = None, **kwargs: Any) -> "Remote":
631+def set_url(
632+self, new_url: str, old_url: Optional[str] = None, allow_unsafe_protocols: bool = False, **kwargs: Any
633+ ) -> "Remote":
615634"""Configure URLs on current remote (cf command git remote set_url)
616635617636 This command manages URLs on the remote.
@@ -620,15 +639,17 @@ def set_url(self, new_url: str, old_url: Optional[str] = None, **kwargs: Any) ->
620639 :param old_url: when set, replaces this URL with new_url for the remote
621640 :return: self
622641 """
642+if not allow_unsafe_protocols:
643+Git.check_unsafe_protocols(new_url)
623644scmd = "set-url"
624645kwargs["insert_kwargs_after"] = scmd
625646if old_url:
626-self.repo.git.remote(scmd, self.name, new_url, old_url, **kwargs)
647+self.repo.git.remote(scmd, "--", self.name, new_url, old_url, **kwargs)
627648else:
628-self.repo.git.remote(scmd, self.name, new_url, **kwargs)
649+self.repo.git.remote(scmd, "--", self.name, new_url, **kwargs)
629650return self
630651631-def add_url(self, url: str, **kwargs: Any) -> "Remote":
652+def add_url(self, url: str, allow_unsafe_protocols: bool = False, **kwargs: Any) -> "Remote":
632653"""Adds a new url on current remote (special case of git remote set_url)
633654634655 This command adds new URLs to a given remote, making it possible to have
@@ -637,6 +658,8 @@ def add_url(self, url: str, **kwargs: Any) -> "Remote":
637658 :param url: string being the URL to add as an extra remote URL
638659 :return: self
639660 """
661+if not allow_unsafe_protocols:
662+Git.check_unsafe_protocols(url)
640663return self.set_url(url, add=True)
641664642665def delete_url(self, url: str, **kwargs: Any) -> "Remote":
@@ -729,7 +752,7 @@ def stale_refs(self) -> IterableList[Reference]:
729752return out_refs
730753731754@classmethod
732-def create(cls, repo: "Repo", name: str, url: str, **kwargs: Any) -> "Remote":
755+def create(cls, repo: "Repo", name: str, url: str, allow_unsafe_protocols: bool = False, **kwargs: Any) -> "Remote":
733756"""Create a new remote to the given repository
734757 :param repo: Repository instance that is to receive the new remote
735758 :param name: Desired name of the remote
@@ -739,7 +762,10 @@ def create(cls, repo: "Repo", name: str, url: str, **kwargs: Any) -> "Remote":
739762 :raise GitCommandError: in case an origin with that name already exists"""
740763scmd = "add"
741764kwargs["insert_kwargs_after"] = scmd
742-repo.git.remote(scmd, name, Git.polish_url(url), **kwargs)
765+url = Git.polish_url(url)
766+if not allow_unsafe_protocols:
767+Git.check_unsafe_protocols(url)
768+repo.git.remote(scmd, "--", name, url, **kwargs)
743769return cls(repo, name)
744770745771# add is an alias
@@ -921,6 +947,8 @@ def fetch(
921947progress: Union[RemoteProgress, None, "UpdateProgress"] = None,
922948verbose: bool = True,
923949kill_after_timeout: Union[None, float] = None,
950+allow_unsafe_protocols: bool = False,
951+allow_unsafe_options: bool = False,
924952**kwargs: Any,
925953 ) -> IterableList[FetchInfo]:
926954"""Fetch the latest changes for this remote
@@ -963,6 +991,14 @@ def fetch(
963991else:
964992args = [refspec]
965993994+if not allow_unsafe_protocols:
995+for ref in args:
996+if ref:
997+Git.check_unsafe_protocols(ref)
998+999+if not allow_unsafe_options:
1000+Git.check_unsafe_options(options=list(kwargs.keys()), unsafe_options=self.unsafe_git_fetch_options)
1001+9661002proc = self.repo.git.fetch(
9671003"--", self, *args, as_process=True, with_stdout=False, universal_newlines=True, v=verbose, **kwargs
9681004 )
@@ -976,6 +1012,8 @@ def pull(
9761012refspec: Union[str, List[str], None] = None,
9771013progress: Union[RemoteProgress, "UpdateProgress", None] = None,
9781014kill_after_timeout: Union[None, float] = None,
1015+allow_unsafe_protocols: bool = False,
1016+allow_unsafe_options: bool = False,
9791017**kwargs: Any,
9801018 ) -> IterableList[FetchInfo]:
9811019"""Pull changes from the given branch, being the same as a fetch followed
@@ -990,6 +1028,16 @@ def pull(
9901028# No argument refspec, then ensure the repo's config has a fetch refspec.
9911029self._assert_refspec()
9921030kwargs = add_progress(kwargs, self.repo.git, progress)
1031+1032+if not allow_unsafe_protocols and refspec:
1033+if isinstance(refspec, str):
1034+Git.check_unsafe_protocols(refspec)
1035+else:
1036+for ref in refspec:
1037+Git.check_unsafe_protocols(ref)
1038+if not allow_unsafe_options:
1039+Git.check_unsafe_options(options=list(kwargs.keys()), unsafe_options=self.unsafe_git_pull_options)
1040+9931041proc = self.repo.git.pull(
9941042"--", self, refspec, with_stdout=False, as_process=True, universal_newlines=True, v=True, **kwargs
9951043 )
@@ -1003,6 +1051,8 @@ def push(
10031051refspec: Union[str, List[str], None] = None,
10041052progress: Union[RemoteProgress, "UpdateProgress", Callable[..., RemoteProgress], None] = None,
10051053kill_after_timeout: Union[None, float] = None,
1054+allow_unsafe_protocols: bool = False,
1055+allow_unsafe_options: bool = False,
10061056**kwargs: Any,
10071057 ) -> IterableList[PushInfo]:
10081058"""Push changes from source branch in refspec to target branch in refspec.
@@ -1033,6 +1083,17 @@ def push(
10331083 If the operation fails completely, the length of the returned IterableList will
10341084 be 0."""
10351085kwargs = add_progress(kwargs, self.repo.git, progress)
1086+1087+if not allow_unsafe_protocols and refspec:
1088+if isinstance(refspec, str):
1089+Git.check_unsafe_protocols(refspec)
1090+else:
1091+for ref in refspec:
1092+Git.check_unsafe_protocols(ref)
1093+1094+if not allow_unsafe_options:
1095+Git.check_unsafe_options(options=list(kwargs.keys()), unsafe_options=self.unsafe_git_push_options)
1096+10361097proc = self.repo.git.push(
10371098"--",
10381099self,