Please find attached the debdiff missing from the original report mail.

Regards, Daniel
diff -Nru setuptools-66.1.1/debian/changelog setuptools-66.1.1/debian/changelog
--- setuptools-66.1.1/debian/changelog	2023-01-27 07:49:44.000000000 +0100
+++ setuptools-66.1.1/debian/changelog	2024-12-31 01:08:15.000000000 +0100
@@ -1,3 +1,13 @@
+setuptools (66.1.1-1+deb12u1) bookworm; urgency=medium
+
+  * Non-maintainer upload by the Debian LTS team.
+  * debian/patches/CVE-2024-6345.patch: Fix CVE-2024-6345.
+    - Replace the unsafe use of os.system to fix a possible remote code
+      execution by supplying malicious URLs in a package index or via the
+      command line.
+
+ -- Daniel Leidert <dleid...@debian.org>  Tue, 31 Dec 2024 01:08:15 +0100
+
 setuptools (66.1.1-1) unstable; urgency=medium
 
   * New upstream version.
diff -Nru setuptools-66.1.1/debian/gbp.conf setuptools-66.1.1/debian/gbp.conf
--- setuptools-66.1.1/debian/gbp.conf	1970-01-01 01:00:00.000000000 +0100
+++ setuptools-66.1.1/debian/gbp.conf	2024-12-31 01:08:15.000000000 +0100
@@ -0,0 +1,4 @@
+[DEFAULT]
+upstream-branch = upstream/bookworm
+debian-branch = debian/bookworm
+pristine-tar = true
diff -Nru setuptools-66.1.1/debian/.gitlab-ci.yml setuptools-66.1.1/debian/.gitlab-ci.yml
--- setuptools-66.1.1/debian/.gitlab-ci.yml	1970-01-01 01:00:00.000000000 +0100
+++ setuptools-66.1.1/debian/.gitlab-ci.yml	2024-12-31 01:08:15.000000000 +0100
@@ -0,0 +1,7 @@
+---
+
+include:
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml
+
+variables:
+  RELEASE: 'bookworm'
diff -Nru setuptools-66.1.1/debian/patches/CVE-2024-6345.patch setuptools-66.1.1/debian/patches/CVE-2024-6345.patch
--- setuptools-66.1.1/debian/patches/CVE-2024-6345.patch	1970-01-01 01:00:00.000000000 +0100
+++ setuptools-66.1.1/debian/patches/CVE-2024-6345.patch	2024-12-31 01:08:15.000000000 +0100
@@ -0,0 +1,296 @@
+From: "Jason R. Coombs" <jar...@jaraco.com>
+Date: Mon, 29 Apr 2024 09:38:31 -0400
+Subject: [PATCH 01/10] .. [PATCH 10/10] Modernize package_index VCS handling
+
+The issue is a possible remote code execution by supplying malicious URLs in a
+package index or via the command line. The issue boils down to unsafe use of
+os.system. Because easy_install and package_index are deprecated, the attack
+surface is smaller, but it's conceivable through social engineering or minor
+compromise to a package index could grant remote access. The fix was released
+in v70.0.0.
+
+Acked-By: Daniel Leidert <dleid...@debian.org>
+Origin: https://github.com/pypa/setuptools/pull/4332
+Bug: https://github.com/pypa/setuptools/issues/4331
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-6345
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2024-6345
+---
+ setuptools/package_index.py           | 145 +++++++++++++++++++---------------
+ setuptools/tests/test_packageindex.py |  34 ++++----
+ 2 files changed, 100 insertions(+), 79 deletions(-)
+
+diff --git a/setuptools/package_index.py b/setuptools/package_index.py
+index bec4183..ea4d640 100644
+--- a/setuptools/package_index.py
++++ b/setuptools/package_index.py
+@@ -1,6 +1,7 @@
+ """PyPI and direct package downloading."""
+ 
+ import sys
++import subprocess
+ import os
+ import re
+ import io
+@@ -586,7 +587,7 @@ class PackageIndex(Environment):
+             scheme = URL_SCHEME(spec)
+             if scheme:
+                 # It's a url, download it to tmpdir
+-                found = self._download_url(scheme.group(1), spec, tmpdir)
++                found = self._download_url(spec, tmpdir)
+                 base, fragment = egg_info_for_url(spec)
+                 if base.endswith('.py'):
+                     found = self.gen_setup(found, fragment, tmpdir)
+@@ -813,7 +814,7 @@ class PackageIndex(Environment):
+             else:
+                 raise DistutilsError("Download error for %s: %s" % (url, v)) from v
+ 
+-    def _download_url(self, scheme, url, tmpdir):
++    def _download_url(self, url, tmpdir):
+         # Determine download filename
+         #
+         name, fragment = egg_info_for_url(url)
+@@ -828,19 +829,58 @@ class PackageIndex(Environment):
+ 
+         filename = os.path.join(tmpdir, name)
+ 
+-        # Download the file
+-        #
+-        if scheme == 'svn' or scheme.startswith('svn+'):
+-            return self._download_svn(url, filename)
+-        elif scheme == 'git' or scheme.startswith('git+'):
+-            return self._download_git(url, filename)
+-        elif scheme.startswith('hg+'):
+-            return self._download_hg(url, filename)
+-        elif scheme == 'file':
+-            return urllib.request.url2pathname(urllib.parse.urlparse(url)[2])
+-        else:
+-            self.url_ok(url, True)  # raises error if not allowed
+-            return self._attempt_download(url, filename)
++        return self._download_vcs(url, filename) or self._download_other(url, filename)
++
++    @staticmethod
++    def _resolve_vcs(url):
++        """
++        >>> rvcs = PackageIndex._resolve_vcs
++        >>> rvcs('git+http://foo/bar')
++        'git'
++        >>> rvcs('hg+https://foo/bar')
++        'hg'
++        >>> rvcs('git:myhost')
++        'git'
++        >>> rvcs('hg:myhost')
++        >>> rvcs('http://foo/bar')
++        """
++        scheme = urllib.parse.urlsplit(url).scheme
++        pre, sep, post = scheme.partition('+')
++        # svn and git have their own protocol; hg does not
++        allowed = set(['svn', 'git'] + ['hg'] * bool(sep))
++        return next(iter({pre} & allowed), None)
++
++
++    def _download_vcs(self, url, spec_filename):
++        vcs = self._resolve_vcs(url)
++        if not vcs:
++            return
++        if vcs == 'svn':
++            return self._download_svn(url, spec_filename)
++
++        filename, _, _ = spec_filename.partition('#')
++        url, rev = self._vcs_split_rev_from_url(url)
++
++        self.info(f"Doing {vcs} clone from {url} to {filename}")
++        subprocess.check_call([vcs, 'clone', '--quiet', url, filename])
++
++        co_commands = dict(
++            git=[vcs, '-C', filename, 'checkout', '--quiet', rev],
++            hg=[vcs, '--cwd', filename, 'up', '-C', '-r', rev, '-q'],
++        )
++        if rev is not None:
++            self.info(f"Checking out {rev}")
++            subprocess.check_call(co_commands[vcs])
++
++        return filename
++
++    def _download_other(self, url, filename):
++        scheme = urllib.parse.urlsplit(url).scheme
++        if scheme == 'file':  # pragma: no cover
++            return urllib.request.url2pathname(urllib.parse.urlparse(url).path)
++        # raise error if not allowed
++        self.url_ok(url, True)
++        return self._attempt_download(url, filename)
+ 
+     def scan_url(self, url):
+         self.process_url(url, True)
+@@ -885,65 +925,40 @@ class PackageIndex(Environment):
+                     netloc = host
+                     parts = scheme, netloc, url, p, q, f
+                     url = urllib.parse.urlunparse(parts)
+-        self.info("Doing subversion checkout from %s to %s", url, filename)
+-        os.system("svn checkout%s -q %s %s" % (creds, url, filename))
++        self.info(f"Doing subversion checkout from {url} to {filename}")
++        subprocess.check_call(['svn', 'checkout', creds, '-q', url, filename])
+         return filename
+ 
+     @staticmethod
+-    def _vcs_split_rev_from_url(url, pop_prefix=False):
+-        scheme, netloc, path, query, frag = urllib.parse.urlsplit(url)
++    def _vcs_split_rev_from_url(url):
++        """
++        Given a possible VCS URL, return a clean URL and resolved revision if any.
++        >>> vsrfu = PackageIndex._vcs_split_rev_from_url
++        >>> vsrfu('git+https://github.com/pypa/setuptools@v69.0.0#egg-info=setuptools')
++        ('https://github.com/pypa/setuptools', 'v69.0.0')
++        >>> vsrfu('git+https://github.com/pypa/setuptools#egg-info=setuptools')
++        ('https://github.com/pypa/setuptools', None)
++        >>> vsrfu('http://foo/bar')
++        ('http://foo/bar', None)
++        """
++        parts = urllib.parse.urlsplit(url)
+ 
+-        scheme = scheme.split('+', 1)[-1]
++        clean_scheme = parts.scheme.split('+', 1)[-1]
+ 
+         # Some fragment identification fails
+-        path = path.split('#', 1)[0]
+-
+-        rev = None
+-        if '@' in path:
+-            path, rev = path.rsplit('@', 1)
+-
+-        # Also, discard fragment
+-        url = urllib.parse.urlunsplit((scheme, netloc, path, query, ''))
+-
+-        return url, rev
+-
+-    def _download_git(self, url, filename):
+-        filename = filename.split('#', 1)[0]
+-        url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
+-
+-        self.info("Doing git clone from %s to %s", url, filename)
+-        os.system("git clone --quiet %s %s" % (url, filename))
+-
+-        if rev is not None:
+-            self.info("Checking out %s", rev)
+-            os.system(
+-                "git -C %s checkout --quiet %s"
+-                % (
+-                    filename,
+-                    rev,
+-                )
+-            )
++        no_fragment_path, _, _ = parts.path.partition('#')
+ 
+-        return filename
++        pre, sep, post = no_fragment_path.rpartition('@')
++        clean_path, rev = (pre, post) if sep else (post, None)
+ 
+-    def _download_hg(self, url, filename):
+-        filename = filename.split('#', 1)[0]
+-        url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
++        resolved = parts._replace(
++            scheme=clean_scheme,
++            path=clean_path,
++            # discard the fragment
++            fragment='',
++        ).geturl()
+ 
+-        self.info("Doing hg clone from %s to %s", url, filename)
+-        os.system("hg clone --quiet %s %s" % (url, filename))
+-
+-        if rev is not None:
+-            self.info("Updating to %s", rev)
+-            os.system(
+-                "hg --cwd %s up -C -r %s -q"
+-                % (
+-                    filename,
+-                    rev,
+-                )
+-            )
+-
+-        return filename
++        return resolved, rev
+ 
+     def debug(self, msg, *args):
+         log.debug(msg, *args)
+diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py
+index 8b5356d..f138d7e 100644
+--- a/setuptools/tests/test_packageindex.py
++++ b/setuptools/tests/test_packageindex.py
+@@ -1,4 +1,5 @@
+ import sys
++import subprocess
+ import os
+ import distutils.errors
+ import platform
+@@ -190,20 +191,23 @@ class TestPackageIndex:
+         url = 'git+https://github.example/group/project@master#egg=foo'
+         index = setuptools.package_index.PackageIndex()
+ 
+-        with mock.patch("os.system") as os_system_mock:
++        with mock.patch("subprocess.check_call") as os_system_mock:
+             result = index.download(url, str(tmpdir))
+ 
+         os_system_mock.assert_called()
+ 
+         expected_dir = str(tmpdir / 'project@master')
+-        expected = (
+-            'git clone --quiet ' 'https://github.example/group/project {expected_dir}'
+-        ).format(**locals())
++        expected = [
++            'git', 'clone', '--quiet',
++            'https://github.example/group/project', f'{expected_dir}'
++        ]
+         first_call_args = os_system_mock.call_args_list[0][0]
+         assert first_call_args == (expected,)
+ 
+-        tmpl = 'git -C {expected_dir} checkout --quiet master'
+-        expected = tmpl.format(**locals())
++        expected = [
++            'git', '-C', f'{expected_dir}',
++            'checkout', '--quiet', 'master'
++        ]
+         assert os_system_mock.call_args_list[1][0] == (expected,)
+         assert result == expected_dir
+ 
+@@ -211,15 +215,16 @@ class TestPackageIndex:
+         url = 'git+https://github.example/group/project#egg=foo'
+         index = setuptools.package_index.PackageIndex()
+ 
+-        with mock.patch("os.system") as os_system_mock:
++        with mock.patch("subprocess.check_call") as os_system_mock:
+             result = index.download(url, str(tmpdir))
+ 
+         os_system_mock.assert_called()
+ 
+         expected_dir = str(tmpdir / 'project')
+-        expected = (
+-            'git clone --quiet ' 'https://github.example/group/project {expected_dir}'
+-        ).format(**locals())
++        expected = [
++            'git', 'clone', '--quiet',
++            'https://github.example/group/project', f'{expected_dir}'
++        ]
+         os_system_mock.assert_called_once_with(expected)
+ 
+     def test_download_svn(self, tmpdir):
+@@ -227,15 +232,16 @@ class TestPackageIndex:
+         index = setuptools.package_index.PackageIndex()
+ 
+         with pytest.warns(UserWarning):
+-            with mock.patch("os.system") as os_system_mock:
++            with mock.patch("subprocess.check_call") as os_system_mock:
+                 result = index.download(url, str(tmpdir))
+ 
+         os_system_mock.assert_called()
+ 
+         expected_dir = str(tmpdir / 'project')
+-        expected = (
+-            'svn checkout -q ' 'svn+https://svn.example/project {expected_dir}'
+-        ).format(**locals())
++        expected = [
++            'svn', 'checkout', '', '-q',
++            'svn+https://svn.example/project', f'{expected_dir}'
++        ]
+         os_system_mock.assert_called_once_with(expected)
+ 
+ 
diff -Nru setuptools-66.1.1/debian/patches/series setuptools-66.1.1/debian/patches/series
--- setuptools-66.1.1/debian/patches/series	2022-09-14 16:54:08.000000000 +0200
+++ setuptools-66.1.1/debian/patches/series	2024-12-31 01:08:15.000000000 +0100
@@ -14,3 +14,4 @@
 sphinx-theme.diff
 no-sphinx-custom-icons.diff
 no-sphinx-hoverxref.diff
+CVE-2024-6345.patch

Attachment: signature.asc
Description: This is a digitally signed message part



Reply via email to