Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit91fdc6c

Browse files
committed
safe mode to disable executing any external programs except git
1 parent85c8155 commit91fdc6c

File tree

4 files changed

+273
-13
lines changed

4 files changed

+273
-13
lines changed

‎git/cmd.py

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
CommandError,
2727
GitCommandError,
2828
GitCommandNotFound,
29+
UnsafeExecutionError,
2930
UnsafeOptionError,
3031
UnsafeProtocolError,
3132
)
@@ -627,6 +628,7 @@ class Git(metaclass=_GitMeta):
627628

628629
__slots__= (
629630
"_working_dir",
631+
"_safe",
630632
"cat_file_all",
631633
"cat_file_header",
632634
"_version_info",
@@ -961,17 +963,56 @@ def check_unsafe_options(cls, options: List[str], unsafe_options: List[str]) ->
961963

962964
CatFileContentStream:TypeAlias=_CatFileContentStream
963965

964-
def__init__(self,working_dir:Union[None,PathLike]=None)->None:
966+
def__init__(self,working_dir:Union[None,PathLike]=None,safe:bool=False)->None:
965967
"""Initialize this instance with:
966968
967969
:param working_dir:
968970
Git directory we should work in. If ``None``, we always work in the current
969971
directory as returned by :func:`os.getcwd`.
970972
This is meant to be the working tree directory if available, or the
971973
``.git`` directory in case of bare repositories.
974+
975+
:param safe:
976+
Lock down the configuration to make it as safe as possible
977+
when working with publicly accessible, untrusted
978+
repositories. This disables all known options that can run
979+
external programs and limits networking to the HTTP protocol
980+
via ``https://`` URLs. This might not cover Git config
981+
options that were added since this was implemented, or
982+
options that have unknown exploit vectors. It is a best
983+
effort defense rather than an exhaustive protection measure.
984+
985+
In order to make this more likely to work with submodules,
986+
some attempts are made to rewrite remote URLs to ``https://``
987+
using `insteadOf` in the config. This might not work on all
988+
projects, so submodules should always use ``https://`` URLs.
989+
990+
:envvar:`GIT_TERMINAL_PROMPT` is set to `false` and these
991+
environment variables are forced to `/bin/true`:
992+
:envvar:`GIT_ASKPASS`, :envvar:`GIT_EDITOR`,
993+
:envvar:`GIT_PAGER`, :envvar:`GIT_SSH`,
994+
:envvar:`GIT_SSH_COMMAND`, and :envvar:`SSH_ASKPASS`.
995+
996+
Git config options are supplied via the command line to set
997+
up key parts of safe mode.
998+
999+
- Direct options for executing external commands are set to ``/bin/true``:
1000+
``core.askpass``, ``core.sshCommand`` and ``credential.helper``.
1001+
1002+
- External password prompts are disabled by skipping authentication using
1003+
``http.emptyAuth=true``.
1004+
1005+
- Any use of an fsmonitor daemon is disabled using ``core.fsmonitor=false``.
1006+
1007+
- Hook scripts are disabled using ``core.hooksPath=/dev/null``.
1008+
1009+
It was not possible to cover all config items that might execute an external
1010+
command, for example, ``receive.procReceiveRefs``,
1011+
``uploadpack.packObjectsHook`` and ``remote.<name>.vcs``.
9721012
"""
9731013
super().__init__()
9741014
self._working_dir=expand_path(working_dir)
1015+
self._safe=safe
9751016
self._git_options:Union[List[str],Tuple[str, ...]]= ()
9761017
self._persistent_git_options:List[str]= []
9771018

@@ -1218,6 +1259,8 @@ def execute(
12181259
12191260
:raise git.exc.GitCommandError:
12201261
1262+
:raise git.exc.UnsafeExecutionError:
1263+
12211264
:note:
12221265
If you add additional keyword arguments to the signature of this method, you
12231266
must update the ``execute_kwargs`` variable housed in this module.
@@ -1227,6 +1270,64 @@ def execute(
12271270
ifself.GIT_PYTHON_TRACEand (self.GIT_PYTHON_TRACE!="full"oras_process):
12281271
_logger.info(" ".join(redacted_command))
12291272

1273+
ifshellisNone:
1274+
# Get the value of USE_SHELL with no deprecation warning. Do this without
1275+
# warnings.catch_warnings, to avoid a race condition with application code
1276+
# configuring warnings. The value could be looked up in type(self).__dict__
1277+
# or Git.__dict__, but those can break under some circumstances. This works
1278+
# the same as self.USE_SHELL in more situations; see Git.__getattribute__.
1279+
shell=super().__getattribute__("USE_SHELL")
1280+
1281+
ifself._safe:
1282+
ifshell:
1283+
raiseUnsafeExecutionError(
1284+
redacted_command,
1285+
"Command cannot be executed in a shell when in safe mode.",
1286+
)
1287+
ifnotisinstance(command,Sequence):
1288+
raiseUnsafeExecutionError(
1289+
redacted_command,
1290+
"Command must be a Sequence to be executed in safe mode.",
1291+
)
1292+
ifcommand[0]!=self.GIT_PYTHON_GIT_EXECUTABLE:
1293+
raiseUnsafeExecutionError(
1294+
redacted_command,
1295+
f'Only "{self.GIT_PYTHON_GIT_EXECUTABLE}" can be executed when in safe mode.',
1296+
)
1297+
config_args= [
1298+
"-c",
1299+
"core.askpass=/bin/true",
1300+
"-c",
1301+
"core.fsmonitor=false",
1302+
"-c",
1303+
"core.hooksPath=/dev/null",
1304+
"-c",
1305+
"core.sshCommand=/bin/true",
1306+
"-c",
1307+
"credential.helper=/bin/true",
1308+
"-c",
1309+
"http.emptyAuth=true",
1310+
"-c",
1311+
"protocol.allow=never",
1312+
"-c",
1313+
"protocol.https.allow=always",
1314+
"-c",
1315+
"url.https://bitbucket.org/.insteadOf=git@bitbucket.org:",
1316+
"-c",
1317+
"url.https://codeberg.org/.insteadOf=git@codeberg.org:",
1318+
"-c",
1319+
"url.https://github.com/.insteadOf=git@github.com:",
1320+
"-c",
1321+
"url.https://gitlab.com/.insteadOf=git@gitlab.com:",
1322+
"-c",
1323+
"url.https://.insteadOf=git://",
1324+
"-c",
1325+
"url.https://.insteadOf=http://",
1326+
"-c",
1327+
"url.https://.insteadOf=ssh://",
1328+
]
1329+
command= [command.pop(0)]+config_args+command
1330+
12301331
# Allow the user to have the command executed in their working dir.
12311332
try:
12321333
cwd=self._working_diroros.getcwd()# type: Union[None, str]
@@ -1244,6 +1345,15 @@ def execute(
12441345
# just to be sure.
12451346
env["LANGUAGE"]="C"
12461347
env["LC_ALL"]="C"
1348+
# Globally disable things that can execute commands, including password prompts.
1349+
ifself._safe:
1350+
env["GIT_ASKPASS"]="/bin/true"
1351+
env["GIT_EDITOR"]="/bin/true"
1352+
env["GIT_PAGER"]="/bin/true"
1353+
env["GIT_SSH"]="/bin/true"
1354+
env["GIT_SSH_COMMAND"]="/bin/true"
1355+
env["GIT_TERMINAL_PROMPT"]="false"
1356+
env["SSH_ASKPASS"]="/bin/true"
12471357
env.update(self._environment)
12481358
ifinline_envisnotNone:
12491359
env.update(inline_env)
@@ -1260,13 +1370,6 @@ def execute(
12601370
# END handle
12611371

12621372
stdout_sink=PIPEifwith_stdoutelsegetattr(subprocess,"DEVNULL",None)oropen(os.devnull,"wb")
1263-
ifshellisNone:
1264-
# Get the value of USE_SHELL with no deprecation warning. Do this without
1265-
# warnings.catch_warnings, to avoid a race condition with application code
1266-
# configuring warnings. The value could be looked up in type(self).__dict__
1267-
# or Git.__dict__, but those can break under some circumstances. This works
1268-
# the same as self.USE_SHELL in more situations; see Git.__getattribute__.
1269-
shell=super().__getattribute__("USE_SHELL")
12701373
_logger.debug(
12711374
"Popen(%s, cwd=%s, stdin=%s, shell=%s, universal_newlines=%s)",
12721375
redacted_command,

‎git/exc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ def __init__(
159159
super().__init__(command,status,stderr,stdout)
160160

161161

162+
classUnsafeExecutionError(CommandError):
163+
"""Thrown if anything but git is executed when in safe mode."""
164+
165+
162166
classCheckoutError(GitError):
163167
"""Thrown if a file could not be checked out from the index as it contained
164168
changes.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp