Skip to content

Commit 03753c0

Browse files
committed
Quote URLs in Repo.clone_from()
Since the URL is passed directly to git clone, and the remote-ext helper will happily execute shell commands, so by default qoute URLs unless a (undocumented, on purpose) kwarg is passed. (CVE-2022-24439) Fixes gitpython-developers#1515
1 parent 17ff263 commit 03753c0

File tree

2 files changed

+16
-0
lines changed

2 files changed

+16
-0
lines changed

Diff for: git/repo/base.py

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import os
99
import re
1010
import shlex
11+
import urllib.parse
1112
import warnings
1213
from gitdb.db.loose import LooseObjectDB
1314

@@ -1272,6 +1273,8 @@ def clone_from(
12721273
git = cls.GitCommandWrapperType(os.getcwd())
12731274
if env is not None:
12741275
git.update_environment(**env)
1276+
if 'unsafe_protocols' not in kwargs:
1277+
url = urllib.parse.quote(url)
12751278
return cls._clone(git, url, to_path, GitCmdObjectDB, progress, multi_options, **kwargs)
12761279

12771280
def archive(

Diff for: test/test_repo.py

+13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import pickle
1414
import sys
1515
import tempfile
16+
import uuid
1617
from unittest import mock, skipIf, SkipTest
1718

1819
import pytest
@@ -263,6 +264,18 @@ def test_leaking_password_in_clone_logs(self, rw_dir):
263264
to_path=rw_dir,
264265
)
265266

267+
@with_rw_repo("HEAD")
268+
def test_clone_from_quotes_urls_by_default(self, repo):
269+
bad_filename = f'{tempfile.gettempdir()}/{uuid.uuid4()}'
270+
bad_url = f'ext::sh -c touch% {bad_filename}'
271+
try:
272+
repo.clone_from(
273+
bad_url, 'tmp', multi_options=["-c protocol.ext.allow=always"]
274+
)
275+
except GitCommandError:
276+
pass
277+
self.assertFalse(pathlib.Path(bad_filename).is_file())
278+
266279
@with_rw_repo("HEAD")
267280
def test_max_chunk_size(self, repo):
268281
class TestOutputStream(TestBase):

0 commit comments

Comments
 (0)