@@ -161,6 +161,20 @@ class Repo:
161161 https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---configltkeygtltvaluegt
162162 """
163163
164+ unsafe_git_archive_options = [
165+ # This option allows arbitrary command execution in git-archive.
166+ "--upload-pack" ,
167+ "--exec" ,
168+ ]
169+ """Options to :manpage:`git-archive(1)` that can be dangerous."""
170+
171+ unsafe_git_revision_options = [
172+ # This option allows output to be written to arbitrary files before revision parsing.
173+ "--output" ,
174+ "-o" ,
175+ ]
176+ """Options to :manpage:`git-rev-list(1)` / :manpage:`git-blame(1)` that can overwrite files."""
177+
164178 # Invariants
165179 config_level : ConfigLevels_Tup = ("system" , "user" , "global" , "repository" )
166180 """Represents the configuration level of a configuration file."""
@@ -775,6 +789,7 @@ def iter_commits(
775789 self ,
776790 rev : Union [str , Commit , "SymbolicReference" , None ] = None ,
777791 paths : Union [PathLike , Sequence [PathLike ]] = "" ,
792+ allow_unsafe_options : bool = False ,
778793 ** kwargs : Any ,
779794 ) -> Iterator [Commit ]:
780795 """An iterator of :class:`~git.objects.commit.Commit` objects representing the
@@ -792,6 +807,9 @@ def iter_commits(
792807 Arguments to be passed to :manpage:`git-rev-list(1)`.
793808 Common ones are ``max_count`` and ``skip``.
794809
810+ :param allow_unsafe_options:
811+ Allow unsafe options in the revision argument, like ``--output``.
812+
795813 :note:
796814 To receive only commits between two named revisions, use the
797815 ``"revA...revB"`` revision specifier.
@@ -802,7 +820,18 @@ def iter_commits(
802820 if rev is None :
803821 rev = self .head .commit
804822
805- return Commit .iter_items (self , rev , paths , ** kwargs )
823+ if not allow_unsafe_options :
824+ Git .check_unsafe_options (
825+ options = [str (rev )] + list (kwargs .keys ()), unsafe_options = self .unsafe_git_revision_options
826+ )
827+
828+ return Commit .iter_items (
829+ self ,
830+ rev ,
831+ paths ,
832+ allow_unsafe_options = allow_unsafe_options ,
833+ ** kwargs ,
834+ )
806835
807836 def merge_base (self , * rev : TBD , ** kwargs : Any ) -> List [Commit ]:
808837 R"""Find the closest common ancestor for the given revision
@@ -1079,7 +1108,9 @@ def active_branch(self) -> Head:
10791108 )
10801109 return active_branch
10811110
1082- def blame_incremental (self , rev : str | HEAD | None , file : str , ** kwargs : Any ) -> Iterator ["BlameEntry" ]:
1111+ def blame_incremental (
1112+ self , rev : str | HEAD | None , file : str , allow_unsafe_options : bool = False , ** kwargs : Any
1113+ ) -> Iterator ["BlameEntry" ]:
10831114 """Iterator for blame information for the given file at the given revision.
10841115
10851116 Unlike :meth:`blame`, this does not return the actual file's contents, only a
@@ -1090,6 +1121,9 @@ def blame_incremental(self, rev: str | HEAD | None, file: str, **kwargs: Any) ->
10901121 uncommitted changes. Otherwise, anything successfully parsed by
10911122 :manpage:`git-rev-parse(1)` is a valid option.
10921123
1124+ :param allow_unsafe_options:
1125+ Allow unsafe options in revision argument, like ``--output``.
1126+
10931127 :return:
10941128 Lazy iterator of :class:`BlameEntry` tuples, where the commit indicates the
10951129 commit to blame for the line, and range indicates a span of line numbers in
@@ -1098,6 +1132,10 @@ def blame_incremental(self, rev: str | HEAD | None, file: str, **kwargs: Any) ->
10981132 If you combine all line number ranges outputted by this command, you should get
10991133 a continuous range spanning all line numbers in the file.
11001134 """
1135+ if not allow_unsafe_options :
1136+ Git .check_unsafe_options (
1137+ options = [str (rev )] + list (kwargs .keys ()), unsafe_options = self .unsafe_git_revision_options
1138+ )
11011139
11021140 data : bytes = self .git .blame (rev , "--" , file , p = True , incremental = True , stdout_as_string = False , ** kwargs )
11031141 commits : Dict [bytes , Commit ] = {}
@@ -1177,6 +1215,7 @@ def blame(
11771215 file : str ,
11781216 incremental : bool = False ,
11791217 rev_opts : Optional [List [str ]] = None ,
1218+ allow_unsafe_options : bool = False ,
11801219 ** kwargs : Any ,
11811220 ) -> List [List [Commit | List [str | bytes ] | None ]] | Iterator [BlameEntry ] | None :
11821221 """The blame information for the given file at the given revision.
@@ -1186,6 +1225,9 @@ def blame(
11861225 uncommitted changes. Otherwise, anything successfully parsed by
11871226 :manpage:`git-rev-parse(1)` is a valid option.
11881227
1228+ :param allow_unsafe_options:
1229+ Allow unsafe options in revision argument, like ``--output``.
1230+
11891231 :return:
11901232 list: [git.Commit, list: [<line>]]
11911233
@@ -1195,7 +1237,12 @@ def blame(
11951237 appearance.
11961238 """
11971239 if incremental :
1198- return self .blame_incremental (rev , file , ** kwargs )
1240+ return self .blame_incremental (rev , file , allow_unsafe_options = allow_unsafe_options , ** kwargs )
1241+ if not allow_unsafe_options :
1242+ rev_opts = rev_opts or []
1243+ Git .check_unsafe_options (
1244+ options = [str (rev )] + rev_opts + list (kwargs .keys ()), unsafe_options = self .unsafe_git_revision_options
1245+ )
11991246 rev_opts = rev_opts or []
12001247 data : bytes = self .git .blame (rev , * rev_opts , "--" , file , p = True , stdout_as_string = False , ** kwargs )
12011248 commits : Dict [str , Commit ] = {}
@@ -1583,6 +1630,7 @@ def archive(
15831630 ostream : Union [TextIO , BinaryIO ],
15841631 treeish : Optional [str ] = None ,
15851632 prefix : Optional [str ] = None ,
1633+ allow_unsafe_options : bool = False ,
15861634 ** kwargs : Any ,
15871635 ) -> Repo :
15881636 """Archive the tree at the given revision.
@@ -1605,6 +1653,9 @@ def archive(
16051653 repository-relative path to a directory or file to place into the archive,
16061654 or a list or tuple of multiple paths.
16071655
1656+ :param allow_unsafe_options:
1657+ Allow unsafe options in ``kwargs``, like ``--exec`` or ``--upload-pack``.
1658+
16081659 :raise git.exc.GitCommandError:
16091660 If something went wrong.
16101661
@@ -1615,6 +1666,8 @@ def archive(
16151666 treeish = self .head .commit
16161667 if prefix and "prefix" not in kwargs :
16171668 kwargs ["prefix" ] = prefix
1669+ if not allow_unsafe_options :
1670+ Git .check_unsafe_options (options = list (kwargs .keys ()), unsafe_options = self .unsafe_git_archive_options )
16181671 kwargs ["output_stream" ] = ostream
16191672 path = kwargs .pop ("path" , [])
16201673 path = cast (Union [PathLike , List [PathLike ], Tuple [PathLike , ...]], path )
0 commit comments