summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/python/python3-setuptools
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-devtools/python/python3-setuptools')
-rw-r--r--meta/recipes-devtools/python/python3-setuptools/CVE-2024-6345.patch312
1 files changed, 312 insertions, 0 deletions
diff --git a/meta/recipes-devtools/python/python3-setuptools/CVE-2024-6345.patch b/meta/recipes-devtools/python/python3-setuptools/CVE-2024-6345.patch
new file mode 100644
index 0000000000..ac520be74a
--- /dev/null
+++ b/meta/recipes-devtools/python/python3-setuptools/CVE-2024-6345.patch
@@ -0,0 +1,312 @@
1From 88807c7062788254f654ea8c03427adc859321f0 Mon Sep 17 00:00:00 2001
2From: Jason R. Coombs <jaraco@jaraco.com>
3Date: Mon Apr 29 20:01:38 2024 -0400
4Subject: [PATCH] Merge pull request #4332 from pypa/debt/package-index-vcs
5
6Modernize package_index VCS handling
7
8CVE: CVE-2024-6345
9
10Upstream-Status: Backport [https://github.com/pypa/setuptools/commit/88807c7062788254f654ea8c03427adc859321f0]
11
12Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
13---
14 setup.cfg | 1 +
15 setuptools/package_index.py | 145 ++++++++++++++------------
16 setuptools/tests/test_packageindex.py | 56 +++++-----
17 3 files changed, 106 insertions(+), 96 deletions(-)
18
19diff --git a/setup.cfg b/setup.cfg
20index edf9798..238d00a 100644
21--- a/setup.cfg
22+++ b/setup.cfg
23@@ -65,6 +65,7 @@ testing =
24 sys_platform != "cygwin"
25 jaraco.develop >= 7.21; python_version >= "3.9" and sys_platform != "cygwin"
26 pytest-home >= 0.5
27+ pytest-subprocess
28 testing-integration =
29 pytest
30 pytest-xdist
31diff --git a/setuptools/package_index.py b/setuptools/package_index.py
32index 271aa97..00a972d 100644
33--- a/setuptools/package_index.py
34+++ b/setuptools/package_index.py
35@@ -1,6 +1,7 @@
36 """PyPI and direct package downloading."""
37
38 import sys
39+import subprocess
40 import os
41 import re
42 import io
43@@ -585,7 +586,7 @@ class PackageIndex(Environment):
44 scheme = URL_SCHEME(spec)
45 if scheme:
46 # It's a url, download it to tmpdir
47- found = self._download_url(scheme.group(1), spec, tmpdir)
48+ found = self._download_url(spec, tmpdir)
49 base, fragment = egg_info_for_url(spec)
50 if base.endswith('.py'):
51 found = self.gen_setup(found, fragment, tmpdir)
52@@ -814,7 +815,7 @@ class PackageIndex(Environment):
53 else:
54 raise DistutilsError("Download error for %s: %s" % (url, v)) from v
55
56- def _download_url(self, scheme, url, tmpdir):
57+ def _download_url(self, url, tmpdir):
58 # Determine download filename
59 #
60 name, fragment = egg_info_for_url(url)
61@@ -829,19 +830,59 @@ class PackageIndex(Environment):
62
63 filename = os.path.join(tmpdir, name)
64
65- # Download the file
66- #
67- if scheme == 'svn' or scheme.startswith('svn+'):
68- return self._download_svn(url, filename)
69- elif scheme == 'git' or scheme.startswith('git+'):
70- return self._download_git(url, filename)
71- elif scheme.startswith('hg+'):
72- return self._download_hg(url, filename)
73- elif scheme == 'file':
74- return urllib.request.url2pathname(urllib.parse.urlparse(url)[2])
75- else:
76- self.url_ok(url, True) # raises error if not allowed
77- return self._attempt_download(url, filename)
78+ return self._download_vcs(url, filename) or self._download_other(url, filename)
79+
80+ @staticmethod
81+ def _resolve_vcs(url):
82+ """
83+ >>> rvcs = PackageIndex._resolve_vcs
84+ >>> rvcs('git+http://foo/bar')
85+ 'git'
86+ >>> rvcs('hg+https://foo/bar')
87+ 'hg'
88+ >>> rvcs('git:myhost')
89+ 'git'
90+ >>> rvcs('hg:myhost')
91+ >>> rvcs('http://foo/bar')
92+ """
93+ scheme = urllib.parse.urlsplit(url).scheme
94+ pre, sep, post = scheme.partition('+')
95+ # svn and git have their own protocol; hg does not
96+ allowed = set(['svn', 'git'] + ['hg'] * bool(sep))
97+ return next(iter({pre} & allowed), None)
98+
99+ def _download_vcs(self, url, spec_filename):
100+ vcs = self._resolve_vcs(url)
101+ if not vcs:
102+ return
103+ if vcs == 'svn':
104+ raise DistutilsError(
105+ f"Invalid config, SVN download is not supported: {url}"
106+ )
107+
108+ filename, _, _ = spec_filename.partition('#')
109+ url, rev = self._vcs_split_rev_from_url(url)
110+
111+ self.info(f"Doing {vcs} clone from {url} to {filename}")
112+ subprocess.check_call([vcs, 'clone', '--quiet', url, filename])
113+
114+ co_commands = dict(
115+ git=[vcs, '-C', filename, 'checkout', '--quiet', rev],
116+ hg=[vcs, '--cwd', filename, 'up', '-C', '-r', rev, '-q'],
117+ )
118+ if rev is not None:
119+ self.info(f"Checking out {rev}")
120+ subprocess.check_call(co_commands[vcs])
121+
122+ return filename
123+
124+ def _download_other(self, url, filename):
125+ scheme = urllib.parse.urlsplit(url).scheme
126+ if scheme == 'file': # pragma: no cover
127+ return urllib.request.url2pathname(urllib.parse.urlparse(url).path)
128+ # raise error if not allowed
129+ self.url_ok(url, True)
130+ return self._attempt_download(url, filename)
131
132 def scan_url(self, url):
133 self.process_url(url, True)
134@@ -857,64 +898,36 @@ class PackageIndex(Environment):
135 os.unlink(filename)
136 raise DistutilsError(f"Unexpected HTML page found at {url}")
137
138- def _download_svn(self, url, _filename):
139- raise DistutilsError(f"Invalid config, SVN download is not supported: {url}")
140-
141 @staticmethod
142- def _vcs_split_rev_from_url(url, pop_prefix=False):
143- scheme, netloc, path, query, frag = urllib.parse.urlsplit(url)
144+ def _vcs_split_rev_from_url(url):
145+ """
146+ Given a possible VCS URL, return a clean URL and resolved revision if any.
147+ >>> vsrfu = PackageIndex._vcs_split_rev_from_url
148+ >>> vsrfu('git+https://github.com/pypa/setuptools@v69.0.0#egg-info=setuptools')
149+ ('https://github.com/pypa/setuptools', 'v69.0.0')
150+ >>> vsrfu('git+https://github.com/pypa/setuptools#egg-info=setuptools')
151+ ('https://github.com/pypa/setuptools', None)
152+ >>> vsrfu('http://foo/bar')
153+ ('http://foo/bar', None)
154+ """
155+ parts = urllib.parse.urlsplit(url)
156
157- scheme = scheme.split('+', 1)[-1]
158+ clean_scheme = parts.scheme.split('+', 1)[-1]
159
160 # Some fragment identification fails
161- path = path.split('#', 1)[0]
162-
163- rev = None
164- if '@' in path:
165- path, rev = path.rsplit('@', 1)
166-
167- # Also, discard fragment
168- url = urllib.parse.urlunsplit((scheme, netloc, path, query, ''))
169-
170- return url, rev
171-
172- def _download_git(self, url, filename):
173- filename = filename.split('#', 1)[0]
174- url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
175-
176- self.info("Doing git clone from %s to %s", url, filename)
177- os.system("git clone --quiet %s %s" % (url, filename))
178-
179- if rev is not None:
180- self.info("Checking out %s", rev)
181- os.system(
182- "git -C %s checkout --quiet %s"
183- % (
184- filename,
185- rev,
186- )
187- )
188+ no_fragment_path, _, _ = parts.path.partition('#')
189
190- return filename
191+ pre, sep, post = no_fragment_path.rpartition('@')
192+ clean_path, rev = (pre, post) if sep else (post, None)
193
194- def _download_hg(self, url, filename):
195- filename = filename.split('#', 1)[0]
196- url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
197+ resolved = parts._replace(
198+ scheme=clean_scheme,
199+ path=clean_path,
200+ # discard the fragment
201+ fragment='',
202+ ).geturl()
203
204- self.info("Doing hg clone from %s to %s", url, filename)
205- os.system("hg clone --quiet %s %s" % (url, filename))
206-
207- if rev is not None:
208- self.info("Updating to %s", rev)
209- os.system(
210- "hg --cwd %s up -C -r %s -q"
211- % (
212- filename,
213- rev,
214- )
215- )
216-
217- return filename
218+ return resolved, rev
219
220 def debug(self, msg, *args):
221 log.debug(msg, *args)
222diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py
223index 41b9661..e4cd91a 100644
224--- a/setuptools/tests/test_packageindex.py
225+++ b/setuptools/tests/test_packageindex.py
226@@ -2,7 +2,6 @@ import distutils.errors
227 import urllib.request
228 import urllib.error
229 import http.client
230-from unittest import mock
231
232 import pytest
233
234@@ -171,49 +170,46 @@ class TestPackageIndex:
235 assert dists[0].version == ''
236 assert dists[1].version == vc
237
238- def test_download_git_with_rev(self, tmpdir):
239+ def test_download_git_with_rev(self, tmp_path, fp):
240 url = 'git+https://github.example/group/project@master#egg=foo'
241 index = setuptools.package_index.PackageIndex()
242
243- with mock.patch("os.system") as os_system_mock:
244- result = index.download(url, str(tmpdir))
245+ expected_dir = tmp_path / 'project@master'
246+ fp.register([
247+ 'git',
248+ 'clone',
249+ '--quiet',
250+ 'https://github.example/group/project',
251+ expected_dir,
252+ ])
253+ fp.register(['git', '-C', expected_dir, 'checkout', '--quiet', 'master'])
254
255- os_system_mock.assert_called()
256+ result = index.download(url, tmp_path)
257
258- expected_dir = str(tmpdir / 'project@master')
259- expected = (
260- 'git clone --quiet ' 'https://github.example/group/project {expected_dir}'
261- ).format(**locals())
262- first_call_args = os_system_mock.call_args_list[0][0]
263- assert first_call_args == (expected,)
264+ assert result == str(expected_dir)
265+ assert len(fp.calls) == 2
266
267- tmpl = 'git -C {expected_dir} checkout --quiet master'
268- expected = tmpl.format(**locals())
269- assert os_system_mock.call_args_list[1][0] == (expected,)
270- assert result == expected_dir
271-
272- def test_download_git_no_rev(self, tmpdir):
273+ def test_download_git_no_rev(self, tmp_path, fp):
274 url = 'git+https://github.example/group/project#egg=foo'
275 index = setuptools.package_index.PackageIndex()
276
277- with mock.patch("os.system") as os_system_mock:
278- result = index.download(url, str(tmpdir))
279-
280- os_system_mock.assert_called()
281-
282- expected_dir = str(tmpdir / 'project')
283- expected = (
284- 'git clone --quiet ' 'https://github.example/group/project {expected_dir}'
285- ).format(**locals())
286- os_system_mock.assert_called_once_with(expected)
287-
288- def test_download_svn(self, tmpdir):
289+ expected_dir = tmp_path / 'project'
290+ fp.register([
291+ 'git',
292+ 'clone',
293+ '--quiet',
294+ 'https://github.example/group/project',
295+ expected_dir,
296+ ])
297+ index.download(url, tmp_path)
298+
299+ def test_download_svn(self, tmp_path):
300 url = 'svn+https://svn.example/project#egg=foo'
301 index = setuptools.package_index.PackageIndex()
302
303 msg = r".*SVN download is not supported.*"
304 with pytest.raises(distutils.errors.DistutilsError, match=msg):
305- index.download(url, str(tmpdir))
306+ index.download(url, tmp_path)
307
308
309 class TestContentCheckers:
310--
3112.40.0
312