@@ -131,6 +131,9 @@ class Repo:
131131 git_dir : PathLike
132132 """The ``.git`` repository directory."""
133133
134+ safe : None
135+ """Whether this is operating using restricted protocol and execution access."""
136+
134137 _common_dir : PathLike = ""
135138
136139 # Precompiled regex
@@ -175,6 +178,7 @@ def __init__(
175178 odbt : Type [LooseObjectDB ] = GitCmdObjectDB ,
176179 search_parent_directories : bool = False ,
177180 expand_vars : bool = True ,
181+ safe : bool = False ,
178182 ) -> None :
179183 R"""Create a new :class:`Repo` instance.
180184
@@ -204,6 +208,44 @@ def __init__(
204208 Please note that this was the default behaviour in older versions of
205209 GitPython, which is considered a bug though.
206210
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+
207249 :raise git.exc.InvalidGitRepositoryError:
208250
209251 :raise git.exc.NoSuchPathError:
@@ -235,6 +277,8 @@ def __init__(
235277 if not os .path .exists (epath ):
236278 raise NoSuchPathError (epath )
237279
280+ self .safe = safe
281+
238282 # Walk up the path to find the `.git` dir.
239283 curpath = epath
240284 git_dir = None
@@ -309,7 +353,7 @@ def __init__(
309353 # END working dir handling
310354
311355 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 )
313357
314358 # Special handling, in special times.
315359 rootpath = osp .join (self .common_dir , "objects" )
@@ -1305,6 +1349,7 @@ def init(
13051349 mkdir : bool = True ,
13061350 odbt : Type [GitCmdObjectDB ] = GitCmdObjectDB ,
13071351 expand_vars : bool = True ,
1352+ safe : bool = False ,
13081353 ** kwargs : Any ,
13091354 ) -> "Repo" :
13101355 """Initialize a git repository at the given path if specified.
@@ -1329,6 +1374,44 @@ def init(
13291374 information disclosure, allowing attackers to access the contents of
13301375 environment variables.
13311376
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+
13321415 :param kwargs:
13331416 Keyword arguments serving as additional options to the
13341417 :manpage:`git-init(1)` command.
@@ -1342,9 +1425,9 @@ def init(
13421425 os .makedirs (path , 0o755 )
13431426
13441427 # git command automatically chdir into the directory
1345- git = cls .GitCommandWrapperType (path )
1428+ git = cls .GitCommandWrapperType (path , safe )
13461429 git .init (** kwargs )
1347- return cls (path , odbt = odbt )
1430+ return cls (path , odbt = odbt , safe = safe )
13481431
13491432 @classmethod
13501433 def _clone (
@@ -1357,6 +1440,7 @@ def _clone(
13571440 multi_options : Optional [List [str ]] = None ,
13581441 allow_unsafe_protocols : bool = False ,
13591442 allow_unsafe_options : bool = False ,
1443+ safe : Union [bool , None ] = None ,
13601444 ** kwargs : Any ,
13611445 ) -> "Repo" :
13621446 odbt = kwargs .pop ("odbt" , odb_default_type )
@@ -1418,7 +1502,11 @@ def _clone(
14181502 if not osp .isabs (path ):
14191503 path = osp .join (git ._working_dir , path ) if git ._working_dir is not None else path
14201504
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 )
14221510
14231511 # Retain env values that were passed to _clone().
14241512 repo .git .update_environment (** git .environment ())
@@ -1501,6 +1589,7 @@ def clone_from(
15011589 multi_options : Optional [List [str ]] = None ,
15021590 allow_unsafe_protocols : bool = False ,
15031591 allow_unsafe_options : bool = False ,
1592+ safe : bool = False ,
15041593 ** kwargs : Any ,
15051594 ) -> "Repo" :
15061595 """Create a clone from the given URL.
@@ -1531,13 +1620,52 @@ def clone_from(
15311620 :param allow_unsafe_options:
15321621 Allow unsafe options to be used, like ``--upload-pack``.
15331622
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+
15341661 :param kwargs:
15351662 See the :meth:`clone` method.
15361663
15371664 :return:
15381665 :class:`Repo` instance pointing to the cloned directory.
1666+
15391667 """
1540- git = cls .GitCommandWrapperType (os .getcwd ())
1668+ git = cls .GitCommandWrapperType (os .getcwd (), safe )
15411669 if env is not None :
15421670 git .update_environment (** env )
15431671 return cls ._clone (
@@ -1549,6 +1677,7 @@ def clone_from(
15491677 multi_options ,
15501678 allow_unsafe_protocols = allow_unsafe_protocols ,
15511679 allow_unsafe_options = allow_unsafe_options ,
1680+ safe = safe ,
15521681 ** kwargs ,
15531682 )
15541683
0 commit comments