21
21
)
22
22
from git .config import GitConfigParser
23
23
from git .db import GitCmdObjectDB
24
- from git .exc import InvalidGitRepositoryError , NoSuchPathError , GitCommandError
24
+ from git .exc import (
25
+ GitCommandError ,
26
+ InvalidGitRepositoryError ,
27
+ NoSuchPathError ,
28
+ UnsafeOptionsUsedError ,
29
+ )
25
30
from git .index import IndexFile
26
31
from git .objects import Submodule , RootModule , Commit
27
32
from git .refs import HEAD , Head , Reference , TagReference
@@ -128,6 +133,7 @@ class Repo(object):
128
133
re_envvars = re .compile (r"(\$(\{\s?)?[a-zA-Z_]\w*(\}\s?)?|%\s?[a-zA-Z_]\w*\s?%)" )
129
134
re_author_committer_start = re .compile (r"^(author|committer)" )
130
135
re_tab_full_line = re .compile (r"^\t(.*)$" )
136
+ re_config_protocol_option = re .compile (r"-[-]?c(|onfig)\s+protocol\." , re .I )
131
137
132
138
# invariants
133
139
# represents the configuration level of a configuration file
@@ -1214,11 +1220,27 @@ def _clone(
1214
1220
# END handle remote repo
1215
1221
return repo
1216
1222
1223
+ @classmethod
1224
+ def unsafe_options (
1225
+ cls ,
1226
+ url : str ,
1227
+ multi_options : Optional [List [str ]] = None ,
1228
+ ) -> bool :
1229
+ if "ext::" in url :
1230
+ return True
1231
+ if multi_options is not None :
1232
+ if any (["--upload-pack" in m for m in multi_options ]):
1233
+ return True
1234
+ if any ([re .match (cls .re_config_protocol_option , m ) for m in multi_options ]):
1235
+ return True
1236
+ return False
1237
+
1217
1238
def clone (
1218
1239
self ,
1219
1240
path : PathLike ,
1220
1241
progress : Optional [Callable ] = None ,
1221
1242
multi_options : Optional [List [str ]] = None ,
1243
+ unsafe_protocols : bool = False ,
1222
1244
** kwargs : Any ,
1223
1245
) -> "Repo" :
1224
1246
"""Create a clone from this repository.
@@ -1229,12 +1251,15 @@ def clone(
1229
1251
option per list item which is passed exactly as specified to clone.
1230
1252
For example ['--config core.filemode=false', '--config core.ignorecase',
1231
1253
'--recurse-submodule=repo1_path', '--recurse-submodule=repo2_path']
1254
+ :param unsafe_protocols: Allow unsafe protocols to be used, like ext
1232
1255
:param kwargs:
1233
1256
* odbt = ObjectDatabase Type, allowing to determine the object database
1234
1257
implementation used by the returned Repo instance
1235
1258
* All remaining keyword arguments are given to the git-clone command
1236
1259
1237
1260
:return: ``git.Repo`` (the newly cloned repo)"""
1261
+ if not unsafe_protocols and self .unsafe_options (path , multi_options ):
1262
+ raise UnsafeOptionsUsedError (f"{ path } requires unsafe_protocols flag" )
1238
1263
return self ._clone (
1239
1264
self .git ,
1240
1265
self .common_dir ,
@@ -1253,6 +1278,7 @@ def clone_from(
1253
1278
progress : Optional [Callable ] = None ,
1254
1279
env : Optional [Mapping [str , str ]] = None ,
1255
1280
multi_options : Optional [List [str ]] = None ,
1281
+ unsafe_protocols : bool = False ,
1256
1282
** kwargs : Any ,
1257
1283
) -> "Repo" :
1258
1284
"""Create a clone from the given URL
@@ -1267,11 +1293,14 @@ def clone_from(
1267
1293
If you want to unset some variable, consider providing empty string
1268
1294
as its value.
1269
1295
:param multi_options: See ``clone`` method
1296
+ :param unsafe_protocols: Allow unsafe protocols to be used, like ext
1270
1297
:param kwargs: see the ``clone`` method
1271
1298
:return: Repo instance pointing to the cloned directory"""
1272
1299
git = cls .GitCommandWrapperType (os .getcwd ())
1273
1300
if env is not None :
1274
1301
git .update_environment (** env )
1302
+ if not unsafe_protocols and cls .unsafe_options (url , multi_options ):
1303
+ raise UnsafeOptionsUsedError (f"{ url } requires unsafe_protocols flag" )
1275
1304
return cls ._clone (git , url , to_path , GitCmdObjectDB , progress , multi_options , ** kwargs )
1276
1305
1277
1306
def archive (
0 commit comments