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

Commita513404

Browse files
committed
Forbid unsafe protocol URLs in Repo.clone{,_from}()
Since the URL is passed directly to git clone, and the remote-ext helperwill happily execute shell commands, so by default disallow URLs thatcontain a "::" unless a new unsafe_protocols kwarg is passed.(CVE-2022-24439)Fixes#1515
1 parent17ff263 commita513404

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

‎git/exc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ class NoSuchPathError(GitError, OSError):
3737
"""Thrown if a path could not be access by the system."""
3838

3939

40+
classUnsafeOptionsUsedError(GitError):
41+
"""Thrown if unsafe protocols or options are passed without overridding."""
42+
43+
4044
classCommandError(GitError):
4145
"""Base class for exceptions thrown at every stage of `Popen()` execution.
4246

‎git/repo/base.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@
2121
)
2222
fromgit.configimportGitConfigParser
2323
fromgit.dbimportGitCmdObjectDB
24-
fromgit.excimportInvalidGitRepositoryError,NoSuchPathError,GitCommandError
24+
fromgit.excimport (
25+
GitCommandError,
26+
InvalidGitRepositoryError,
27+
NoSuchPathError,
28+
UnsafeOptionsUsedError,
29+
)
2530
fromgit.indeximportIndexFile
2631
fromgit.objectsimportSubmodule,RootModule,Commit
2732
fromgit.refsimportHEAD,Head,Reference,TagReference
@@ -128,6 +133,7 @@ class Repo(object):
128133
re_envvars=re.compile(r"(\$(\{\s?)?[a-zA-Z_]\w*(\}\s?)?|%\s?[a-zA-Z_]\w*\s?%)")
129134
re_author_committer_start=re.compile(r"^(author|committer)")
130135
re_tab_full_line=re.compile(r"^\t(.*)$")
136+
re_config_protocol_option=re.compile(r"-[-]?c(|onfig)\s+protocol\.",re.I)
131137

132138
# invariants
133139
# represents the configuration level of a configuration file
@@ -1214,11 +1220,27 @@ def _clone(
12141220
# END handle remote repo
12151221
returnrepo
12161222

1223+
@classmethod
1224+
defunsafe_options(
1225+
cls,
1226+
url:str,
1227+
multi_options:Optional[List[str]]=None,
1228+
)->bool:
1229+
if"ext::"inurl:
1230+
returnTrue
1231+
ifmulti_optionsisnotNone:
1232+
ifany(["--upload-pack"inmforminmulti_options]):
1233+
returnTrue
1234+
ifany([re.match(cls.re_config_protocol_option,m)forminmulti_options]):
1235+
returnTrue
1236+
returnFalse
1237+
12171238
defclone(
12181239
self,
12191240
path:PathLike,
12201241
progress:Optional[Callable]=None,
12211242
multi_options:Optional[List[str]]=None,
1243+
unsafe_protocols:bool=False,
12221244
**kwargs:Any,
12231245
)->"Repo":
12241246
"""Create a clone from this repository.
@@ -1229,12 +1251,15 @@ def clone(
12291251
option per list item which is passed exactly as specified to clone.
12301252
For example ['--config core.filemode=false', '--config core.ignorecase',
12311253
'--recurse-submodule=repo1_path', '--recurse-submodule=repo2_path']
1254+
:param unsafe_protocols: Allow unsafe protocols to be used, like ext
12321255
:param kwargs:
12331256
* odbt = ObjectDatabase Type, allowing to determine the object database
12341257
implementation used by the returned Repo instance
12351258
* All remaining keyword arguments are given to the git-clone command
12361259
12371260
:return: ``git.Repo`` (the newly cloned repo)"""
1261+
ifnotunsafe_protocolsandself.unsafe_options(url,multi_options):
1262+
raiseUnsafeOptionsUsedError(f"{url} requires unsafe_protocols flag")
12381263
returnself._clone(
12391264
self.git,
12401265
self.common_dir,
@@ -1253,6 +1278,7 @@ def clone_from(
12531278
progress:Optional[Callable]=None,
12541279
env:Optional[Mapping[str,str]]=None,
12551280
multi_options:Optional[List[str]]=None,
1281+
unsafe_protocols:bool=False,
12561282
**kwargs:Any,
12571283
)->"Repo":
12581284
"""Create a clone from the given URL
@@ -1267,11 +1293,14 @@ def clone_from(
12671293
If you want to unset some variable, consider providing empty string
12681294
as its value.
12691295
:param multi_options: See ``clone`` method
1296+
:param unsafe_protocols: Allow unsafe protocols to be used, like ext
12701297
:param kwargs: see the ``clone`` method
12711298
:return: Repo instance pointing to the cloned directory"""
12721299
git=cls.GitCommandWrapperType(os.getcwd())
12731300
ifenvisnotNone:
12741301
git.update_environment(**env)
1302+
ifnotunsafe_protocolsandcls.unsafe_options(url,multi_options):
1303+
raiseUnsafeOptionsUsedError(f"{url} requires unsafe_protocols flag")
12751304
returncls._clone(git,url,to_path,GitCmdObjectDB,progress,multi_options,**kwargs)
12761305

12771306
defarchive(

‎test/test_repo.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
importpickle
1414
importsys
1515
importtempfile
16+
importuuid
1617
fromunittestimportmock,skipIf,SkipTest
1718

1819
importpytest
@@ -37,6 +38,7 @@
3738
)
3839
fromgit.excimport (
3940
BadObject,
41+
UnsafeOptionsUsedError,
4042
)
4143
fromgit.repo.funimporttouch
4244
fromtest.libimportTestBase,with_rw_repo,fixture
@@ -263,6 +265,40 @@ def test_leaking_password_in_clone_logs(self, rw_dir):
263265
to_path=rw_dir,
264266
)
265267

268+
deftest_unsafe_options(self):
269+
self.assertFalse(Repo.unsafe_options("github.com/deploy/deploy"))
270+
271+
deftest_unsafe_options_ext_url(self):
272+
self.assertTrue(Repo.unsafe_options("ext::ssh"))
273+
274+
deftest_unsafe_options_multi_options_upload_pack(self):
275+
self.assertTrue(Repo.unsafe_options("", ["--upload-pack='touch foo'"]))
276+
277+
deftest_unsafe_options_multi_options_config_user(self):
278+
self.assertFalse(Repo.unsafe_options("", ["--config user"]))
279+
280+
deftest_unsafe_options_multi_options_config_protocol(self):
281+
self.assertTrue(Repo.unsafe_options("", ["--config protocol.foo"]))
282+
283+
deftest_clone_from_forbids_helper_urls_by_default(self):
284+
withself.assertRaises(UnsafeOptionsUsedError):
285+
Repo.clone_from("ext::sh -c touch% /tmp/foo","tmp")
286+
287+
@with_rw_repo("HEAD")
288+
deftest_clone_from_allow_unsafe(self,repo):
289+
bad_filename=pathlib.Path(f'{tempfile.gettempdir()}/{uuid.uuid4()}')
290+
bad_url=f'ext::sh -c touch%{bad_filename}'
291+
try:
292+
repo.clone_from(
293+
bad_url,'tmp',
294+
multi_options=["-c protocol.ext.allow=always"],
295+
unsafe_protocols=True
296+
)
297+
exceptGitCommandError:
298+
pass
299+
self.assertTrue(bad_filename.is_file())
300+
bad_filename.unlink()
301+
266302
@with_rw_repo("HEAD")
267303
deftest_max_chunk_size(self,repo):
268304
classTestOutputStream(TestBase):

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp