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
@@ -1215,11 +1221,27 @@ def _clone(
1215
1221
# END handle remote repo
1216
1222
return repo
1217
1223
1224
+ @classmethod
1225
+ def unsafe_options (
1226
+ cls ,
1227
+ url : str ,
1228
+ multi_options : Optional [List [str ]] = None ,
1229
+ ) -> bool :
1230
+ if "ext::" in url :
1231
+ return True
1232
+ if multi_options is not None :
1233
+ if any (["--upload-pack" in m for m in multi_options ]):
1234
+ return True
1235
+ if any ([re .match (cls .re_config_protocol_option , m ) for m in multi_options ]):
1236
+ return True
1237
+ return False
1238
+
1218
1239
def clone (
1219
1240
self ,
1220
1241
path : PathLike ,
1221
1242
progress : Optional [Callable ] = None ,
1222
1243
multi_options : Optional [List [str ]] = None ,
1244
+ unsafe_protocols : bool = False ,
1223
1245
** kwargs : Any ,
1224
1246
) -> "Repo" :
1225
1247
"""Create a clone from this repository.
@@ -1230,12 +1252,15 @@ def clone(
1230
1252
option per list item which is passed exactly as specified to clone.
1231
1253
For example ['--config core.filemode=false', '--config core.ignorecase',
1232
1254
'--recurse-submodule=repo1_path', '--recurse-submodule=repo2_path']
1255
+ :param unsafe_protocols: Allow unsafe protocols to be used, like ext
1233
1256
:param kwargs:
1234
1257
* odbt = ObjectDatabase Type, allowing to determine the object database
1235
1258
implementation used by the returned Repo instance
1236
1259
* All remaining keyword arguments are given to the git-clone command
1237
1260
1238
1261
:return: ``git.Repo`` (the newly cloned repo)"""
1262
+ if not unsafe_protocols and self .unsafe_options (path , multi_options ):
1263
+ raise UnsafeOptionsUsedError (f"{ path } requires unsafe_protocols flag" )
1239
1264
return self ._clone (
1240
1265
self .git ,
1241
1266
self .common_dir ,
@@ -1254,6 +1279,7 @@ def clone_from(
1254
1279
progress : Optional [Callable ] = None ,
1255
1280
env : Optional [Mapping [str , str ]] = None ,
1256
1281
multi_options : Optional [List [str ]] = None ,
1282
+ unsafe_protocols : bool = False ,
1257
1283
** kwargs : Any ,
1258
1284
) -> "Repo" :
1259
1285
"""Create a clone from the given URL
@@ -1268,11 +1294,14 @@ def clone_from(
1268
1294
If you want to unset some variable, consider providing empty string
1269
1295
as its value.
1270
1296
:param multi_options: See ``clone`` method
1297
+ :param unsafe_protocols: Allow unsafe protocols to be used, like ext
1271
1298
:param kwargs: see the ``clone`` method
1272
1299
:return: Repo instance pointing to the cloned directory"""
1273
1300
git = cls .GitCommandWrapperType (os .getcwd ())
1274
1301
if env is not None :
1275
1302
git .update_environment (** env )
1303
+ if not unsafe_protocols and cls .unsafe_options (url , multi_options ):
1304
+ raise UnsafeOptionsUsedError (f"{ url } requires unsafe_protocols flag" )
1276
1305
return cls ._clone (git , url , to_path , GitCmdObjectDB , progress , multi_options , ** kwargs )
1277
1306
1278
1307
def archive (
0 commit comments