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

Commit9dcf381

Browse files
committed
safe mode to disable executing any external programs except git
1 parent8576534 commit9dcf381

File tree

3 files changed

+101
-6
lines changed

3 files changed

+101
-6
lines changed

‎git/cmd.py

Lines changed: 62 additions & 1 deletion
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
)
@@ -398,6 +399,7 @@ class Git(metaclass=_GitMeta):
398399

399400
__slots__= (
400401
"_working_dir",
402+
"_safe",
401403
"cat_file_all",
402404
"cat_file_header",
403405
"_version_info",
@@ -944,17 +946,20 @@ def __del__(self) -> None:
944946
self._stream.read(bytes_left+1)
945947
# END handle incomplete read
946948

947-
def__init__(self,working_dir:Union[None,PathLike]=None)->None:
949+
def__init__(self,working_dir:Union[None,PathLike]=None,safe:bool=False)->None:
948950
"""Initialize this instance with:
949951
950952
:param working_dir:
951953
Git directory we should work in. If ``None``, we always work in the current
952954
directory as returned by :func:`os.getcwd`.
953955
This is meant to be the working tree directory if available, or the
954956
``.git`` directory in case of bare repositories.
957+
958+
TODO :param safe:
955959
"""
956960
super().__init__()
957961
self._working_dir=expand_path(working_dir)
962+
self._safe=safe
958963
self._git_options:Union[List[str],Tuple[str, ...]]= ()
959964
self._persistent_git_options:List[str]= []
960965

@@ -1201,6 +1206,8 @@ def execute(
12011206
12021207
:raise git.exc.GitCommandError:
12031208
1209+
:raise git.exc.UnsafeExecutionError:
1210+
12041211
:note:
12051212
If you add additional keyword arguments to the signature of this method, you
12061213
must update the ``execute_kwargs`` variable housed in this module.
@@ -1210,6 +1217,51 @@ def execute(
12101217
ifself.GIT_PYTHON_TRACEand (self.GIT_PYTHON_TRACE!="full"oras_process):
12111218
_logger.info(" ".join(redacted_command))
12121219

1220+
ifself._safe:
1221+
ifisinstance(command,str)orcommand[0]!=self.GIT_PYTHON_GIT_EXECUTABLE:
1222+
raiseUnsafeExecutionError(
1223+
redacted_command,
1224+
f'Only{self.GIT_PYTHON_GIT_EXECUTABLE} can be executed when in safe mode.',
1225+
)
1226+
ifshell:
1227+
raiseUnsafeExecutionError(
1228+
redacted_command,
1229+
f'Command cannot be executed in a shell when in safe mode.',
1230+
)
1231+
config_args= [
1232+
"-c",
1233+
"core.askpass=/bin/true",
1234+
"-c",
1235+
"core.fsmonitor=false",
1236+
"-c",
1237+
"core.hooksPath=/dev/null",
1238+
"-c",
1239+
"core.sshCommand=/bin/true",
1240+
"-c",
1241+
"credential.helper=/bin/true",
1242+
"-c",
1243+
"http.emptyAuth=true",
1244+
"-c",
1245+
"protocol.allow=never",
1246+
"-c",
1247+
"protocol.https.allow=always",
1248+
"-c",
1249+
"url.https://bitbucket.org/.insteadOf=git@bitbucket.org:",
1250+
"-c",
1251+
"url.https://codeberg.org/.insteadOf=git@codeberg.org:",
1252+
"-c",
1253+
"url.https://github.com/.insteadOf=git@github.com:",
1254+
"-c",
1255+
"url.https://gitlab.com/.insteadOf=git@gitlab.com:",
1256+
"-c",
1257+
"url.https://.insteadOf=git://",
1258+
"-c",
1259+
"url.https://.insteadOf=http://",
1260+
"-c",
1261+
"url.https://.insteadOf=ssh://",
1262+
]
1263+
command= [command.pop(0)]+config_args+command
1264+
12131265
# Allow the user to have the command executed in their working dir.
12141266
try:
12151267
cwd=self._working_diroros.getcwd()# type: Union[None, str]
@@ -1227,6 +1279,15 @@ def execute(
12271279
# just to be sure.
12281280
env["LANGUAGE"]="C"
12291281
env["LC_ALL"]="C"
1282+
# Globally disable things that can execute commands, including password prompts.
1283+
ifself._safe:
1284+
env["GIT_ASKPASS"]="/bin/true"
1285+
env["GIT_EDITOR"]="/bin/true"
1286+
env["GIT_PAGER"]="/bin/true"
1287+
env["GIT_SSH"]="/bin/true"
1288+
env["GIT_SSH_COMMAND"]="/bin/true"
1289+
env["GIT_TERMINAL_PROMPT"]="false"
1290+
env["SSH_ASKPASS"]="/bin/true"
12301291
env.update(self._environment)
12311292
ifinline_envisnotNone:
12321293
env.update(inline_env)

‎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.

‎git/repo/base.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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,17 @@ 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+
an external program and limits networking to the HTTP
216+
protocol via https:// URLs. This might not cover Git config
217+
options that were added since this was implemented, or
218+
options that might have unknown exploit vectors. It is a
219+
best effort defense rather than an exhaustive protection
220+
measure.
221+
207222
:raise git.exc.InvalidGitRepositoryError:
208223
209224
:raise git.exc.NoSuchPathError:
@@ -235,6 +250,8 @@ def __init__(
235250
ifnotos.path.exists(epath):
236251
raiseNoSuchPathError(epath)
237252

253+
self.safe=safe
254+
238255
# Walk up the path to find the `.git` dir.
239256
curpath=epath
240257
git_dir=None
@@ -309,7 +326,7 @@ def __init__(
309326
# END working dir handling
310327

311328
self.working_dir:PathLike=self._working_tree_dirorself.common_dir
312-
self.git=self.GitCommandWrapperType(self.working_dir)
329+
self.git=self.GitCommandWrapperType(self.working_dir,safe)
313330

314331
# Special handling, in special times.
315332
rootpath=osp.join(self.common_dir,"objects")
@@ -1305,6 +1322,7 @@ def init(
13051322
mkdir:bool=True,
13061323
odbt:Type[GitCmdObjectDB]=GitCmdObjectDB,
13071324
expand_vars:bool=True,
1325+
safe:bool=False,
13081326
**kwargs:Any,
13091327
)->"Repo":
13101328
"""Initialize a git repository at the given path if specified.
@@ -1329,6 +1347,8 @@ def init(
13291347
information disclosure, allowing attackers to access the contents of
13301348
environment variables.
13311349
1350+
TODO :param safe:
1351+
13321352
:param kwargs:
13331353
Keyword arguments serving as additional options to the
13341354
:manpage:`git-init(1)` command.
@@ -1342,9 +1362,9 @@ def init(
13421362
os.makedirs(path,0o755)
13431363

13441364
# git command automatically chdir into the directory
1345-
git=cls.GitCommandWrapperType(path)
1365+
git=cls.GitCommandWrapperType(path,safe)
13461366
git.init(**kwargs)
1347-
returncls(path,odbt=odbt)
1367+
returncls(path,odbt=odbt,safe=safe)
13481368

13491369
@classmethod
13501370
def_clone(
@@ -1357,6 +1377,7 @@ def _clone(
13571377
multi_options:Optional[List[str]]=None,
13581378
allow_unsafe_protocols:bool=False,
13591379
allow_unsafe_options:bool=False,
1380+
safe:Union[bool,None]=None,
13601381
**kwargs:Any,
13611382
)->"Repo":
13621383
odbt=kwargs.pop("odbt",odb_default_type)
@@ -1418,7 +1439,11 @@ def _clone(
14181439
ifnotosp.isabs(path):
14191440
path=osp.join(git._working_dir,path)ifgit._working_dirisnotNoneelsepath
14201441

1421-
repo=cls(path,odbt=odbt)
1442+
# if safe is not explicitly defined, then the new Repo instance should inherit the safe value
1443+
ifsafeisNone:
1444+
safe=git._safe
1445+
1446+
repo=cls(path,odbt=odbt,safe=safe)
14221447

14231448
# Retain env values that were passed to _clone().
14241449
repo.git.update_environment(**git.environment())
@@ -1501,6 +1526,7 @@ def clone_from(
15011526
multi_options:Optional[List[str]]=None,
15021527
allow_unsafe_protocols:bool=False,
15031528
allow_unsafe_options:bool=False,
1529+
safe:bool=False,
15041530
**kwargs:Any,
15051531
)->"Repo":
15061532
"""Create a clone from the given URL.
@@ -1531,13 +1557,16 @@ def clone_from(
15311557
:param allow_unsafe_options:
15321558
Allow unsafe options to be used, like ``--upload-pack``.
15331559
1560+
:param safe:
1561+
TODO
1562+
15341563
:param kwargs:
15351564
See the :meth:`clone` method.
15361565
15371566
:return:
15381567
:class:`Repo` instance pointing to the cloned directory.
15391568
"""
1540-
git=cls.GitCommandWrapperType(os.getcwd())
1569+
git=cls.GitCommandWrapperType(os.getcwd(),safe)
15411570
ifenvisnotNone:
15421571
git.update_environment(**env)
15431572
returncls._clone(
@@ -1549,6 +1578,7 @@ def clone_from(
15491578
multi_options,
15501579
allow_unsafe_protocols=allow_unsafe_protocols,
15511580
allow_unsafe_options=allow_unsafe_options,
1581+
safe=safe,
15521582
**kwargs,
15531583
)
15541584

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp