@@ -131,6 +131,9 @@ class Repo:
131
131
git_dir :PathLike
132
132
"""The ``.git`` repository directory."""
133
133
134
+ safe :None
135
+ """Whether this is operating using restricted protocol and execution access."""
136
+
134
137
_common_dir :PathLike = ""
135
138
136
139
# Precompiled regex
@@ -175,6 +178,7 @@ def __init__(
175
178
odbt :Type [LooseObjectDB ]= GitCmdObjectDB ,
176
179
search_parent_directories :bool = False ,
177
180
expand_vars :bool = True ,
181
+ safe :bool = False ,
178
182
)-> None :
179
183
R"""Create a new :class:`Repo` instance.
180
184
@@ -204,6 +208,44 @@ def __init__(
204
208
Please note that this was the default behaviour in older versions of
205
209
GitPython, which is considered a bug though.
206
210
211
+ :param safe:
212
+ Lock down the configuration to make it as safe as possible
213
+ when working with publicly accessible, untrusted
214
+ repositories. This disables all known options that can run
215
+ external programs and limits networking to the HTTP protocol
216
+ via ``https://`` URLs. This might not cover Git config
217
+ options that were added since this was implemented, or
218
+ options that have unknown exploit vectors. It is a best
219
+ effort defense rather than an exhaustive protection measure.
220
+
221
+ In order to make this more likely to work with submodules,
222
+ some attempts are made to rewrite remote URLs to ``https://``
223
+ using `insteadOf` in the config. This might not work on all
224
+ projects, so submodules should always use ``https://`` URLs.
225
+
226
+ :envvar:`GIT_TERMINAL_PROMPT` is set to `false` and these
227
+ environment variables are forced to `/bin/true`:
228
+ :envvar:`GIT_ASKPASS`, :envvar:`GIT_EDITOR`,
229
+ :envvar:`GIT_PAGER`, :envvar:`GIT_SSH`,
230
+ :envvar:`GIT_SSH_COMMAND`, and :envvar:`SSH_ASKPASS`.
231
+
232
+ Git config options are supplied via the command line to set
233
+ up key parts of safe mode.
234
+
235
+ - Direct options for executing external commands are set to ``/bin/true``:
236
+ ``core.askpass``, ``core.sshCommand`` and ``credential.helper``.
237
+
238
+ - External password prompts are disabled by skipping authentication using
239
+ ``http.emptyAuth=true``.
240
+
241
+ - Any use of an fsmonitor daemon is disabled using ``core.fsmonitor=false``.
242
+
243
+ - Hook scripts are disabled using ``core.hooksPath=/dev/null``.
244
+
245
+ It was not possible to cover all config items that might execute an external
246
+ command, for example, ``receive.procReceiveRefs``,
247
+ ``uploadpack.packObjectsHook`` and ``remote.<name>.vcs``.
248
+
207
249
:raise git.exc.InvalidGitRepositoryError:
208
250
209
251
:raise git.exc.NoSuchPathError:
@@ -235,6 +277,8 @@ def __init__(
235
277
if not os .path .exists (epath ):
236
278
raise NoSuchPathError (epath )
237
279
280
+ self .safe = safe
281
+
238
282
# Walk up the path to find the `.git` dir.
239
283
curpath = epath
240
284
git_dir = None
@@ -309,7 +353,7 @@ def __init__(
309
353
# END working dir handling
310
354
311
355
self .working_dir :PathLike = self ._working_tree_dir or self .common_dir
312
- self .git = self .GitCommandWrapperType (self .working_dir )
356
+ self .git = self .GitCommandWrapperType (self .working_dir , safe )
313
357
314
358
# Special handling, in special times.
315
359
rootpath = osp .join (self .common_dir ,"objects" )
@@ -1305,6 +1349,7 @@ def init(
1305
1349
mkdir :bool = True ,
1306
1350
odbt :Type [GitCmdObjectDB ]= GitCmdObjectDB ,
1307
1351
expand_vars :bool = True ,
1352
+ safe :bool = False ,
1308
1353
** kwargs :Any ,
1309
1354
)-> "Repo" :
1310
1355
"""Initialize a git repository at the given path if specified.
@@ -1329,6 +1374,44 @@ def init(
1329
1374
information disclosure, allowing attackers to access the contents of
1330
1375
environment variables.
1331
1376
1377
+ :param safe:
1378
+ Lock down the configuration to make it as safe as possible
1379
+ when working with publicly accessible, untrusted
1380
+ repositories. This disables all known options that can run
1381
+ external programs and limits networking to the HTTP protocol
1382
+ via ``https://`` URLs. This might not cover Git config
1383
+ options that were added since this was implemented, or
1384
+ options that have unknown exploit vectors. It is a best
1385
+ effort defense rather than an exhaustive protection measure.
1386
+
1387
+ In order to make this more likely to work with submodules,
1388
+ some attempts are made to rewrite remote URLs to ``https://``
1389
+ using `insteadOf` in the config. This might not work on all
1390
+ projects, so submodules should always use ``https://`` URLs.
1391
+
1392
+ :envvar:`GIT_TERMINAL_PROMPT` is set to `false` and these
1393
+ environment variables are forced to `/bin/true`:
1394
+ :envvar:`GIT_ASKPASS`, :envvar:`GIT_EDITOR`,
1395
+ :envvar:`GIT_PAGER`, :envvar:`GIT_SSH`,
1396
+ :envvar:`GIT_SSH_COMMAND`, and :envvar:`SSH_ASKPASS`.
1397
+
1398
+ Git config options are supplied via the command line to set
1399
+ up key parts of safe mode.
1400
+
1401
+ - Direct options for executing external commands are set to ``/bin/true``:
1402
+ ``core.askpass``, ``core.sshCommand`` and ``credential.helper``.
1403
+
1404
+ - External password prompts are disabled by skipping authentication using
1405
+ ``http.emptyAuth=true``.
1406
+
1407
+ - Any use of an fsmonitor daemon is disabled using ``core.fsmonitor=false``.
1408
+
1409
+ - Hook scripts are disabled using ``core.hooksPath=/dev/null``.
1410
+
1411
+ It was not possible to cover all config items that might execute an external
1412
+ command, for example, ``receive.procReceiveRefs``,
1413
+ ``uploadpack.packObjectsHook`` and ``remote.<name>.vcs``.
1414
+
1332
1415
:param kwargs:
1333
1416
Keyword arguments serving as additional options to the
1334
1417
:manpage:`git-init(1)` command.
@@ -1342,9 +1425,9 @@ def init(
1342
1425
os .makedirs (path ,0o755 )
1343
1426
1344
1427
# git command automatically chdir into the directory
1345
- git = cls .GitCommandWrapperType (path )
1428
+ git = cls .GitCommandWrapperType (path , safe )
1346
1429
git .init (** kwargs )
1347
- return cls (path ,odbt = odbt )
1430
+ return cls (path ,odbt = odbt , safe = safe )
1348
1431
1349
1432
@classmethod
1350
1433
def _clone (
@@ -1357,6 +1440,7 @@ def _clone(
1357
1440
multi_options :Optional [List [str ]]= None ,
1358
1441
allow_unsafe_protocols :bool = False ,
1359
1442
allow_unsafe_options :bool = False ,
1443
+ safe :Union [bool ,None ]= None ,
1360
1444
** kwargs :Any ,
1361
1445
)-> "Repo" :
1362
1446
odbt = kwargs .pop ("odbt" ,odb_default_type )
@@ -1418,7 +1502,11 @@ def _clone(
1418
1502
if not osp .isabs (path ):
1419
1503
path = osp .join (git ._working_dir ,path )if git ._working_dir is not None else path
1420
1504
1421
- repo = cls (path ,odbt = odbt )
1505
+ # if safe is not explicitly defined, then the new Repo instance should inherit the safe value
1506
+ if safe is None :
1507
+ safe = git ._safe
1508
+
1509
+ repo = cls (path ,odbt = odbt ,safe = safe )
1422
1510
1423
1511
# Retain env values that were passed to _clone().
1424
1512
repo .git .update_environment (** git .environment ())
@@ -1501,6 +1589,7 @@ def clone_from(
1501
1589
multi_options :Optional [List [str ]]= None ,
1502
1590
allow_unsafe_protocols :bool = False ,
1503
1591
allow_unsafe_options :bool = False ,
1592
+ safe :bool = False ,
1504
1593
** kwargs :Any ,
1505
1594
)-> "Repo" :
1506
1595
"""Create a clone from the given URL.
@@ -1531,13 +1620,52 @@ def clone_from(
1531
1620
:param allow_unsafe_options:
1532
1621
Allow unsafe options to be used, like ``--upload-pack``.
1533
1622
1623
+ :param safe:
1624
+ Lock down the configuration to make it as safe as possible
1625
+ when working with publicly accessible, untrusted
1626
+ repositories. This disables all known options that can run
1627
+ external programs and limits networking to the HTTP protocol
1628
+ via ``https://`` URLs. This might not cover Git config
1629
+ options that were added since this was implemented, or
1630
+ options that have unknown exploit vectors. It is a best
1631
+ effort defense rather than an exhaustive protection measure.
1632
+
1633
+ In order to make this more likely to work with submodules,
1634
+ some attempts are made to rewrite remote URLs to ``https://``
1635
+ using `insteadOf` in the config. This might not work on all
1636
+ projects, so submodules should always use ``https://`` URLs.
1637
+
1638
+ :envvar:`GIT_TERMINAL_PROMPT` is set to `false` and these
1639
+ environment variables are forced to `/bin/true`:
1640
+ :envvar:`GIT_ASKPASS`, :envvar:`GIT_EDITOR`,
1641
+ :envvar:`GIT_PAGER`, :envvar:`GIT_SSH`,
1642
+ :envvar:`GIT_SSH_COMMAND`, and :envvar:`SSH_ASKPASS`.
1643
+
1644
+ Git config options are supplied via the command line to set
1645
+ up key parts of safe mode.
1646
+
1647
+ - Direct options for executing external commands are set to ``/bin/true``:
1648
+ ``core.askpass``, ``core.sshCommand`` and ``credential.helper``.
1649
+
1650
+ - External password prompts are disabled by skipping authentication using
1651
+ ``http.emptyAuth=true``.
1652
+
1653
+ - Any use of an fsmonitor daemon is disabled using ``core.fsmonitor=false``.
1654
+
1655
+ - Hook scripts are disabled using ``core.hooksPath=/dev/null``.
1656
+
1657
+ It was not possible to cover all config items that might execute an external
1658
+ command, for example, ``receive.procReceiveRefs``,
1659
+ ``uploadpack.packObjectsHook`` and ``remote.<name>.vcs``.
1660
+
1534
1661
:param kwargs:
1535
1662
See the :meth:`clone` method.
1536
1663
1537
1664
:return:
1538
1665
:class:`Repo` instance pointing to the cloned directory.
1666
+
1539
1667
"""
1540
- git = cls .GitCommandWrapperType (os .getcwd ())
1668
+ git = cls .GitCommandWrapperType (os .getcwd (), safe )
1541
1669
if env is not None :
1542
1670
git .update_environment (** env )
1543
1671
return cls ._clone (
@@ -1549,6 +1677,7 @@ def clone_from(
1549
1677
multi_options ,
1550
1678
allow_unsafe_protocols = allow_unsafe_protocols ,
1551
1679
allow_unsafe_options = allow_unsafe_options ,
1680
+ safe = safe ,
1552
1681
** kwargs ,
1553
1682
)
1554
1683