Compare commits
11 Commits
Author | SHA256 | Date | |
---|---|---|---|
efa8febd12 | |||
59c649b520 | |||
8a73c83002 | |||
db9a09b8c2 | |||
1716dfe088 | |||
e761be5380 | |||
47f6e77cdd | |||
8a3ae3ab7b | |||
c2077eafb7 | |||
|
3d3eac0a3e | ||
f4d7952bf7 |
335
CVE-2024-6923-email-hdr-inject.patch
Normal file
335
CVE-2024-6923-email-hdr-inject.patch
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
From ff3629b3cedf5e50a7a5f567283778fe5539a11e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Petr Viktorin <encukou@gmail.com>
|
||||||
|
Date: Wed, 31 Jul 2024 00:19:48 +0200
|
||||||
|
Subject: [PATCH] [3.10] gh-121650: Encode newlines in headers, and verify
|
||||||
|
headers are sound (GH-122233)
|
||||||
|
|
||||||
|
Per RFC 2047:
|
||||||
|
|
||||||
|
> [...] these encoding schemes allow the
|
||||||
|
> encoding of arbitrary octet values, mail readers that implement this
|
||||||
|
> decoding should also ensure that display of the decoded data on the
|
||||||
|
> recipient's terminal will not cause unwanted side-effects
|
||||||
|
|
||||||
|
It seems that the "quoted-word" scheme is a valid way to include
|
||||||
|
a newline character in a header value, just like we already allow
|
||||||
|
undecodable bytes or control characters.
|
||||||
|
They do need to be properly quoted when serialized to text, though.
|
||||||
|
|
||||||
|
This should fail for custom fold() implementations that aren't careful
|
||||||
|
about newlines.
|
||||||
|
|
||||||
|
(cherry picked from commit 097633981879b3c9de9a1dd120d3aa585ecc2384)
|
||||||
|
|
||||||
|
Co-authored-by: Petr Viktorin <encukou@gmail.com>
|
||||||
|
Co-authored-by: Bas Bloemsaat <bas@bloemsaat.org>
|
||||||
|
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
||||||
|
---
|
||||||
|
Doc/library/email.errors.rst | 6
|
||||||
|
Doc/library/email.policy.rst | 18 ++
|
||||||
|
Doc/whatsnew/3.10.rst | 12 +
|
||||||
|
Lib/email/_header_value_parser.py | 12 +
|
||||||
|
Lib/email/_policybase.py | 8 +
|
||||||
|
Lib/email/errors.py | 4
|
||||||
|
Lib/email/generator.py | 13 +-
|
||||||
|
Lib/test/test_email/test_generator.py | 62 ++++++++++
|
||||||
|
Lib/test/test_email/test_policy.py | 26 ++++
|
||||||
|
Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst | 5
|
||||||
|
10 files changed, 162 insertions(+), 4 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst
|
||||||
|
|
||||||
|
--- a/Doc/library/email.errors.rst
|
||||||
|
+++ b/Doc/library/email.errors.rst
|
||||||
|
@@ -59,6 +59,12 @@ The following exception classes are defi
|
||||||
|
:class:`~email.mime.image.MIMEImage`).
|
||||||
|
|
||||||
|
|
||||||
|
+.. exception:: HeaderWriteError()
|
||||||
|
+
|
||||||
|
+ Raised when an error occurs when the :mod:`~email.generator` outputs
|
||||||
|
+ headers.
|
||||||
|
+
|
||||||
|
+
|
||||||
|
Here is the list of the defects that the :class:`~email.parser.FeedParser`
|
||||||
|
can find while parsing messages. Note that the defects are added to the message
|
||||||
|
where the problem was found, so for example, if a message nested inside a
|
||||||
|
--- a/Doc/library/email.policy.rst
|
||||||
|
+++ b/Doc/library/email.policy.rst
|
||||||
|
@@ -229,6 +229,24 @@ added matters. To illustrate::
|
||||||
|
|
||||||
|
.. versionadded:: 3.6
|
||||||
|
|
||||||
|
+
|
||||||
|
+ .. attribute:: verify_generated_headers
|
||||||
|
+
|
||||||
|
+ If ``True`` (the default), the generator will raise
|
||||||
|
+ :exc:`~email.errors.HeaderWriteError` instead of writing a header
|
||||||
|
+ that is improperly folded or delimited, such that it would
|
||||||
|
+ be parsed as multiple headers or joined with adjacent data.
|
||||||
|
+ Such headers can be generated by custom header classes or bugs
|
||||||
|
+ in the ``email`` module.
|
||||||
|
+
|
||||||
|
+ As it's a security feature, this defaults to ``True`` even in the
|
||||||
|
+ :class:`~email.policy.Compat32` policy.
|
||||||
|
+ For backwards compatible, but unsafe, behavior, it must be set to
|
||||||
|
+ ``False`` explicitly.
|
||||||
|
+
|
||||||
|
+ .. versionadded:: 3.10.15
|
||||||
|
+
|
||||||
|
+
|
||||||
|
The following :class:`Policy` method is intended to be called by code using
|
||||||
|
the email library to create policy instances with custom settings:
|
||||||
|
|
||||||
|
--- a/Doc/whatsnew/3.10.rst
|
||||||
|
+++ b/Doc/whatsnew/3.10.rst
|
||||||
|
@@ -2357,3 +2357,15 @@ ipaddress
|
||||||
|
|
||||||
|
* Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``,
|
||||||
|
``IPv6Address``, ``IPv4Network`` and ``IPv6Network``.
|
||||||
|
+
|
||||||
|
+email
|
||||||
|
+-----
|
||||||
|
+
|
||||||
|
+* Headers with embedded newlines are now quoted on output.
|
||||||
|
+
|
||||||
|
+ The :mod:`~email.generator` will now refuse to serialize (write) headers
|
||||||
|
+ that are improperly folded or delimited, such that they would be parsed as
|
||||||
|
+ multiple headers or joined with adjacent data.
|
||||||
|
+ If you need to turn this safety feature off,
|
||||||
|
+ set :attr:`~email.policy.Policy.verify_generated_headers`.
|
||||||
|
+ (Contributed by Bas Bloemsaat and Petr Viktorin in :gh:`121650`.)
|
||||||
|
--- a/Lib/email/_header_value_parser.py
|
||||||
|
+++ b/Lib/email/_header_value_parser.py
|
||||||
|
@@ -92,6 +92,8 @@ TOKEN_ENDS = TSPECIALS | WSP
|
||||||
|
ASPECIALS = TSPECIALS | set("*'%")
|
||||||
|
ATTRIBUTE_ENDS = ASPECIALS | WSP
|
||||||
|
EXTENDED_ATTRIBUTE_ENDS = ATTRIBUTE_ENDS - set('%')
|
||||||
|
+NLSET = {'\n', '\r'}
|
||||||
|
+SPECIALSNL = SPECIALS | NLSET
|
||||||
|
|
||||||
|
def quote_string(value):
|
||||||
|
return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"'
|
||||||
|
@@ -2778,9 +2780,13 @@ def _refold_parse_tree(parse_tree, *, po
|
||||||
|
wrap_as_ew_blocked -= 1
|
||||||
|
continue
|
||||||
|
tstr = str(part)
|
||||||
|
- if part.token_type == 'ptext' and set(tstr) & SPECIALS:
|
||||||
|
- # Encode if tstr contains special characters.
|
||||||
|
- want_encoding = True
|
||||||
|
+ if not want_encoding:
|
||||||
|
+ if part.token_type == 'ptext':
|
||||||
|
+ # Encode if tstr contains special characters.
|
||||||
|
+ want_encoding = not SPECIALSNL.isdisjoint(tstr)
|
||||||
|
+ else:
|
||||||
|
+ # Encode if tstr contains newlines.
|
||||||
|
+ want_encoding = not NLSET.isdisjoint(tstr)
|
||||||
|
try:
|
||||||
|
tstr.encode(encoding)
|
||||||
|
charset = encoding
|
||||||
|
--- a/Lib/email/_policybase.py
|
||||||
|
+++ b/Lib/email/_policybase.py
|
||||||
|
@@ -157,6 +157,13 @@ class Policy(_PolicyBase, metaclass=abc.
|
||||||
|
message_factory -- the class to use to create new message objects.
|
||||||
|
If the value is None, the default is Message.
|
||||||
|
|
||||||
|
+ verify_generated_headers
|
||||||
|
+ -- if true, the generator verifies that each header
|
||||||
|
+ they are properly folded, so that a parser won't
|
||||||
|
+ treat it as multiple headers, start-of-body, or
|
||||||
|
+ part of another header.
|
||||||
|
+ This is a check against custom Header & fold()
|
||||||
|
+ implementations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise_on_defect = False
|
||||||
|
@@ -165,6 +172,7 @@ class Policy(_PolicyBase, metaclass=abc.
|
||||||
|
max_line_length = 78
|
||||||
|
mangle_from_ = False
|
||||||
|
message_factory = None
|
||||||
|
+ verify_generated_headers = True
|
||||||
|
|
||||||
|
def handle_defect(self, obj, defect):
|
||||||
|
"""Based on policy, either raise defect or call register_defect.
|
||||||
|
--- a/Lib/email/errors.py
|
||||||
|
+++ b/Lib/email/errors.py
|
||||||
|
@@ -29,6 +29,10 @@ class CharsetError(MessageError):
|
||||||
|
"""An illegal charset was given."""
|
||||||
|
|
||||||
|
|
||||||
|
+class HeaderWriteError(MessageError):
|
||||||
|
+ """Error while writing headers."""
|
||||||
|
+
|
||||||
|
+
|
||||||
|
# These are parsing defects which the parser was able to work around.
|
||||||
|
class MessageDefect(ValueError):
|
||||||
|
"""Base class for a message defect."""
|
||||||
|
--- a/Lib/email/generator.py
|
||||||
|
+++ b/Lib/email/generator.py
|
||||||
|
@@ -14,12 +14,14 @@ import random
|
||||||
|
from copy import deepcopy
|
||||||
|
from io import StringIO, BytesIO
|
||||||
|
from email.utils import _has_surrogates
|
||||||
|
+from email.errors import HeaderWriteError
|
||||||
|
|
||||||
|
UNDERSCORE = '_'
|
||||||
|
NL = '\n' # XXX: no longer used by the code below.
|
||||||
|
|
||||||
|
NLCRE = re.compile(r'\r\n|\r|\n')
|
||||||
|
fcre = re.compile(r'^From ', re.MULTILINE)
|
||||||
|
+NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -223,7 +225,16 @@ class Generator:
|
||||||
|
|
||||||
|
def _write_headers(self, msg):
|
||||||
|
for h, v in msg.raw_items():
|
||||||
|
- self.write(self.policy.fold(h, v))
|
||||||
|
+ folded = self.policy.fold(h, v)
|
||||||
|
+ if self.policy.verify_generated_headers:
|
||||||
|
+ linesep = self.policy.linesep
|
||||||
|
+ if not folded.endswith(self.policy.linesep):
|
||||||
|
+ raise HeaderWriteError(
|
||||||
|
+ f'folded header does not end with {linesep!r}: {folded!r}')
|
||||||
|
+ if NEWLINE_WITHOUT_FWSP.search(folded.removesuffix(linesep)):
|
||||||
|
+ raise HeaderWriteError(
|
||||||
|
+ f'folded header contains newline: {folded!r}')
|
||||||
|
+ self.write(folded)
|
||||||
|
# A blank line always separates headers from body
|
||||||
|
self.write(self._NL)
|
||||||
|
|
||||||
|
--- a/Lib/test/test_email/test_generator.py
|
||||||
|
+++ b/Lib/test/test_email/test_generator.py
|
||||||
|
@@ -6,6 +6,7 @@ from email.message import EmailMessage
|
||||||
|
from email.generator import Generator, BytesGenerator
|
||||||
|
from email.headerregistry import Address
|
||||||
|
from email import policy
|
||||||
|
+import email.errors
|
||||||
|
from test.test_email import TestEmailBase, parameterize
|
||||||
|
|
||||||
|
|
||||||
|
@@ -216,6 +217,44 @@ class TestGeneratorBase:
|
||||||
|
g.flatten(msg)
|
||||||
|
self.assertEqual(s.getvalue(), self.typ(expected))
|
||||||
|
|
||||||
|
+ def test_keep_encoded_newlines(self):
|
||||||
|
+ msg = self.msgmaker(self.typ(textwrap.dedent("""\
|
||||||
|
+ To: nobody
|
||||||
|
+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com
|
||||||
|
+
|
||||||
|
+ None
|
||||||
|
+ """)))
|
||||||
|
+ expected = textwrap.dedent("""\
|
||||||
|
+ To: nobody
|
||||||
|
+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com
|
||||||
|
+
|
||||||
|
+ None
|
||||||
|
+ """)
|
||||||
|
+ s = self.ioclass()
|
||||||
|
+ g = self.genclass(s, policy=self.policy.clone(max_line_length=80))
|
||||||
|
+ g.flatten(msg)
|
||||||
|
+ self.assertEqual(s.getvalue(), self.typ(expected))
|
||||||
|
+
|
||||||
|
+ def test_keep_long_encoded_newlines(self):
|
||||||
|
+ msg = self.msgmaker(self.typ(textwrap.dedent("""\
|
||||||
|
+ To: nobody
|
||||||
|
+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com
|
||||||
|
+
|
||||||
|
+ None
|
||||||
|
+ """)))
|
||||||
|
+ expected = textwrap.dedent("""\
|
||||||
|
+ To: nobody
|
||||||
|
+ Subject: Bad subject
|
||||||
|
+ =?utf-8?q?=0A?=Bcc:
|
||||||
|
+ injection@example.com
|
||||||
|
+
|
||||||
|
+ None
|
||||||
|
+ """)
|
||||||
|
+ s = self.ioclass()
|
||||||
|
+ g = self.genclass(s, policy=self.policy.clone(max_line_length=30))
|
||||||
|
+ g.flatten(msg)
|
||||||
|
+ self.assertEqual(s.getvalue(), self.typ(expected))
|
||||||
|
+
|
||||||
|
|
||||||
|
class TestGenerator(TestGeneratorBase, TestEmailBase):
|
||||||
|
|
||||||
|
@@ -224,6 +263,29 @@ class TestGenerator(TestGeneratorBase, T
|
||||||
|
ioclass = io.StringIO
|
||||||
|
typ = str
|
||||||
|
|
||||||
|
+ def test_verify_generated_headers(self):
|
||||||
|
+ """gh-121650: by default the generator prevents header injection"""
|
||||||
|
+ class LiteralHeader(str):
|
||||||
|
+ name = 'Header'
|
||||||
|
+ def fold(self, **kwargs):
|
||||||
|
+ return self
|
||||||
|
+
|
||||||
|
+ for text in (
|
||||||
|
+ 'Value\r\nBad Injection\r\n',
|
||||||
|
+ 'NoNewLine'
|
||||||
|
+ ):
|
||||||
|
+ with self.subTest(text=text):
|
||||||
|
+ message = message_from_string(
|
||||||
|
+ "Header: Value\r\n\r\nBody",
|
||||||
|
+ policy=self.policy,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ del message['Header']
|
||||||
|
+ message['Header'] = LiteralHeader(text)
|
||||||
|
+
|
||||||
|
+ with self.assertRaises(email.errors.HeaderWriteError):
|
||||||
|
+ message.as_string()
|
||||||
|
+
|
||||||
|
|
||||||
|
class TestBytesGenerator(TestGeneratorBase, TestEmailBase):
|
||||||
|
|
||||||
|
--- a/Lib/test/test_email/test_policy.py
|
||||||
|
+++ b/Lib/test/test_email/test_policy.py
|
||||||
|
@@ -26,6 +26,7 @@ class PolicyAPITests(unittest.TestCase):
|
||||||
|
'raise_on_defect': False,
|
||||||
|
'mangle_from_': True,
|
||||||
|
'message_factory': None,
|
||||||
|
+ 'verify_generated_headers': True,
|
||||||
|
}
|
||||||
|
# These default values are the ones set on email.policy.default.
|
||||||
|
# If any of these defaults change, the docs must be updated.
|
||||||
|
@@ -277,6 +278,31 @@ class PolicyAPITests(unittest.TestCase):
|
||||||
|
with self.assertRaises(email.errors.HeaderParseError):
|
||||||
|
policy.fold("Subject", subject)
|
||||||
|
|
||||||
|
+ def test_verify_generated_headers(self):
|
||||||
|
+ """Turning protection off allows header injection"""
|
||||||
|
+ policy = email.policy.default.clone(verify_generated_headers=False)
|
||||||
|
+ for text in (
|
||||||
|
+ 'Header: Value\r\nBad: Injection\r\n',
|
||||||
|
+ 'Header: NoNewLine'
|
||||||
|
+ ):
|
||||||
|
+ with self.subTest(text=text):
|
||||||
|
+ message = email.message_from_string(
|
||||||
|
+ "Header: Value\r\n\r\nBody",
|
||||||
|
+ policy=policy,
|
||||||
|
+ )
|
||||||
|
+ class LiteralHeader(str):
|
||||||
|
+ name = 'Header'
|
||||||
|
+ def fold(self, **kwargs):
|
||||||
|
+ return self
|
||||||
|
+
|
||||||
|
+ del message['Header']
|
||||||
|
+ message['Header'] = LiteralHeader(text)
|
||||||
|
+
|
||||||
|
+ self.assertEqual(
|
||||||
|
+ message.as_string(),
|
||||||
|
+ f"{text}\nBody",
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
# XXX: Need subclassing tests.
|
||||||
|
# For adding subclassed objects, make sure the usual rules apply (subclass
|
||||||
|
# wins), but that the order still works (right overrides left).
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst
|
||||||
|
@@ -0,0 +1,5 @@
|
||||||
|
+:mod:`email` headers with embedded newlines are now quoted on output. The
|
||||||
|
+:mod:`~email.generator` will now refuse to serialize (write) headers that
|
||||||
|
+are unsafely folded or delimited; see
|
||||||
|
+:attr:`~email.policy.Policy.verify_generated_headers`. (Contributed by Bas
|
||||||
|
+Bloemsaat and Petr Viktorin in :gh:`121650`.)
|
136
CVE-2024-8088-inf-loop-zipfile_Path.patch
Normal file
136
CVE-2024-8088-inf-loop-zipfile_Path.patch
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
---
|
||||||
|
Lib/test/test_zipfile.py | 75 ++++++++++
|
||||||
|
Lib/zipfile.py | 10 +
|
||||||
|
Misc/NEWS.d/next/Library/2024-08-11-14-08-04.gh-issue-122905.7tDsxA.rst | 1
|
||||||
|
Misc/NEWS.d/next/Library/2024-08-26-13-45-20.gh-issue-123270.gXHvNJ.rst | 3
|
||||||
|
4 files changed, 87 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
--- a/Lib/test/test_zipfile.py
|
||||||
|
+++ b/Lib/test/test_zipfile.py
|
||||||
|
@@ -3280,6 +3280,81 @@ with zipfile.ZipFile(io.BytesIO(), "w")
|
||||||
|
zipfile.Path(zf)
|
||||||
|
zf.extractall(source_path.parent)
|
||||||
|
|
||||||
|
+ def test_malformed_paths(self):
|
||||||
|
+ """
|
||||||
|
+ Path should handle malformed paths gracefully.
|
||||||
|
+
|
||||||
|
+ Paths with leading slashes are not visible.
|
||||||
|
+
|
||||||
|
+ Paths with dots are treated like regular files.
|
||||||
|
+ """
|
||||||
|
+ data = io.BytesIO()
|
||||||
|
+ zf = zipfile.ZipFile(data, "w")
|
||||||
|
+ zf.writestr("../parent.txt", b"content")
|
||||||
|
+ zf.filename = ''
|
||||||
|
+ root = zipfile.Path(zf)
|
||||||
|
+ assert list(map(str, root.iterdir())) == ['../']
|
||||||
|
+ assert root.joinpath('..').joinpath('parent.txt').read_bytes() == b'content'
|
||||||
|
+
|
||||||
|
+ def test_unsupported_names(self):
|
||||||
|
+ """
|
||||||
|
+ Path segments with special characters are readable.
|
||||||
|
+
|
||||||
|
+ On some platforms or file systems, characters like
|
||||||
|
+ ``:`` and ``?`` are not allowed, but they are valid
|
||||||
|
+ in the zip file.
|
||||||
|
+ """
|
||||||
|
+ data = io.BytesIO()
|
||||||
|
+ zf = zipfile.ZipFile(data, "w")
|
||||||
|
+ zf.writestr("path?", b"content")
|
||||||
|
+ zf.writestr("V: NMS.flac", b"fLaC...")
|
||||||
|
+ zf.filename = ''
|
||||||
|
+ root = zipfile.Path(zf)
|
||||||
|
+ contents = root.iterdir()
|
||||||
|
+ assert next(contents).name == 'path?'
|
||||||
|
+ assert next(contents).name == 'V: NMS.flac'
|
||||||
|
+ assert root.joinpath('V: NMS.flac').read_bytes() == b"fLaC..."
|
||||||
|
+
|
||||||
|
+ def test_backslash_not_separator(self):
|
||||||
|
+ """
|
||||||
|
+ In a zip file, backslashes are not separators.
|
||||||
|
+ """
|
||||||
|
+ data = io.BytesIO()
|
||||||
|
+ zf = zipfile.ZipFile(data, "w")
|
||||||
|
+ zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content")
|
||||||
|
+ zf.filename = ''
|
||||||
|
+ root = zipfile.Path(zf)
|
||||||
|
+ (first,) = root.iterdir()
|
||||||
|
+ assert not first.is_dir()
|
||||||
|
+ assert first.name == 'foo\\bar'
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class DirtyZipInfo(zipfile.ZipInfo):
|
||||||
|
+ """
|
||||||
|
+ Bypass name sanitization.
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ def __init__(self, filename, *args, **kwargs):
|
||||||
|
+ super().__init__(filename, *args, **kwargs)
|
||||||
|
+ self.filename = filename
|
||||||
|
+
|
||||||
|
+ @classmethod
|
||||||
|
+ def for_name(cls, name, archive):
|
||||||
|
+ """
|
||||||
|
+ Construct the same way that ZipFile.writestr does.
|
||||||
|
+
|
||||||
|
+ TODO: extract this functionality and re-use
|
||||||
|
+ """
|
||||||
|
+ self = cls(filename=name, date_time=time.localtime(time.time())[:6])
|
||||||
|
+ self.compress_type = archive.compression
|
||||||
|
+ self.compress_level = archive.compresslevel
|
||||||
|
+ if self.filename.endswith('/'): # pragma: no cover
|
||||||
|
+ self.external_attr = 0o40775 << 16 # drwxrwxr-x
|
||||||
|
+ self.external_attr |= 0x10 # MS-DOS directory flag
|
||||||
|
+ else:
|
||||||
|
+ self.external_attr = 0o600 << 16 # ?rw-------
|
||||||
|
+ return self
|
||||||
|
+
|
||||||
|
|
||||||
|
class StripExtraTests(unittest.TestCase):
|
||||||
|
# Note: all of the "z" characters are technically invalid, but up
|
||||||
|
--- a/Lib/zipfile.py
|
||||||
|
+++ b/Lib/zipfile.py
|
||||||
|
@@ -9,6 +9,7 @@ import io
|
||||||
|
import itertools
|
||||||
|
import os
|
||||||
|
import posixpath
|
||||||
|
+import re
|
||||||
|
import shutil
|
||||||
|
import stat
|
||||||
|
import struct
|
||||||
|
@@ -2151,7 +2152,7 @@ def _parents(path):
|
||||||
|
def _ancestry(path):
|
||||||
|
"""
|
||||||
|
Given a path with elements separated by
|
||||||
|
- posixpath.sep, generate all elements of that path
|
||||||
|
+ posixpath.sep, generate all elements of that path.
|
||||||
|
|
||||||
|
>>> list(_ancestry('b/d'))
|
||||||
|
['b/d', 'b']
|
||||||
|
@@ -2163,9 +2164,14 @@ def _ancestry(path):
|
||||||
|
['b']
|
||||||
|
>>> list(_ancestry(''))
|
||||||
|
[]
|
||||||
|
+
|
||||||
|
+ Multiple separators are treated like a single.
|
||||||
|
+
|
||||||
|
+ >>> list(_ancestry('//b//d///f//'))
|
||||||
|
+ ['//b//d///f', '//b//d', '//b']
|
||||||
|
"""
|
||||||
|
path = path.rstrip(posixpath.sep)
|
||||||
|
- while path and path != posixpath.sep:
|
||||||
|
+ while path.rstrip(posixpath.sep):
|
||||||
|
yield path
|
||||||
|
path, tail = posixpath.split(path)
|
||||||
|
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2024-08-11-14-08-04.gh-issue-122905.7tDsxA.rst
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+:class:`zipfile.Path` objects now sanitize names from the zipfile.
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2024-08-26-13-45-20.gh-issue-123270.gXHvNJ.rst
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+Applied a more surgical fix for malformed payloads in :class:`zipfile.Path`
|
||||||
|
+causing infinite loops (gh-122905) without breaking contents using
|
||||||
|
+legitimate characters.
|
@ -13,8 +13,10 @@ Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
|||||||
Lib/site.py | 9 ++++++++-
|
Lib/site.py | 9 ++++++++-
|
||||||
2 files changed, 21 insertions(+), 3 deletions(-)
|
2 files changed, 21 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
--- a/Lib/distutils/command/install.py
|
Index: Python-3.10.14/Lib/distutils/command/install.py
|
||||||
+++ b/Lib/distutils/command/install.py
|
===================================================================
|
||||||
|
--- Python-3.10.14.orig/Lib/distutils/command/install.py
|
||||||
|
+++ Python-3.10.14/Lib/distutils/command/install.py
|
||||||
@@ -441,8 +441,19 @@ class install(Command):
|
@@ -441,8 +441,19 @@ class install(Command):
|
||||||
raise DistutilsOptionError(
|
raise DistutilsOptionError(
|
||||||
"must not supply exec-prefix without prefix")
|
"must not supply exec-prefix without prefix")
|
||||||
@ -37,8 +39,10 @@ Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
if self.exec_prefix is None:
|
if self.exec_prefix is None:
|
||||||
--- a/Lib/site.py
|
Index: Python-3.10.14/Lib/site.py
|
||||||
+++ b/Lib/site.py
|
===================================================================
|
||||||
|
--- Python-3.10.14.orig/Lib/site.py
|
||||||
|
+++ Python-3.10.14/Lib/site.py
|
||||||
@@ -390,8 +390,15 @@ def getsitepackages(prefixes=None):
|
@@ -390,8 +390,15 @@ def getsitepackages(prefixes=None):
|
||||||
return sitepackages
|
return sitepackages
|
||||||
|
|
||||||
@ -56,3 +60,128 @@ Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
|||||||
for sitedir in getsitepackages(prefixes):
|
for sitedir in getsitepackages(prefixes):
|
||||||
if os.path.isdir(sitedir):
|
if os.path.isdir(sitedir):
|
||||||
addsitedir(sitedir, known_paths)
|
addsitedir(sitedir, known_paths)
|
||||||
|
Index: Python-3.10.14/Lib/sysconfig.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.10.14.orig/Lib/sysconfig.py
|
||||||
|
+++ Python-3.10.14/Lib/sysconfig.py
|
||||||
|
@@ -117,6 +117,19 @@ if _HAS_USER_BASE:
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
+# This is used by distutils.command.install in the stdlib
|
||||||
|
+# as well as pypa/distutils (e.g. bundled in setuptools).
|
||||||
|
+# The self.prefix value is set to sys.prefix + /local/
|
||||||
|
+# if neither RPM build nor virtual environment is
|
||||||
|
+# detected to make distutils install packages
|
||||||
|
+# into the separate location.
|
||||||
|
+# https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
+if (not (hasattr(sys, 'real_prefix') or
|
||||||
|
+ sys.prefix != sys.base_prefix) and
|
||||||
|
+ 'RPM_BUILD_ROOT' not in os.environ):
|
||||||
|
+ _prefix_addition = '/local'
|
||||||
|
+
|
||||||
|
+
|
||||||
|
_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
|
||||||
|
'scripts', 'data')
|
||||||
|
|
||||||
|
@@ -136,6 +149,16 @@ _variable_rx = r"([a-zA-Z][a-zA-Z0-9_]+)
|
||||||
|
_findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)"
|
||||||
|
_findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}"
|
||||||
|
|
||||||
|
+# For a brief period of time in the Fedora 36 life cycle,
|
||||||
|
+# this installation scheme existed and was documented in the release notes.
|
||||||
|
+# For backwards compatibility, we keep it here (at least on 3.10 and 3.11).
|
||||||
|
+_INSTALL_SCHEMES['rpm_prefix'] = _INSTALL_SCHEMES['posix_prefix']
|
||||||
|
+
|
||||||
|
+# For a brief period of time in the Fedora 36 life cycle,
|
||||||
|
+# this installation scheme existed and was documented in the release notes.
|
||||||
|
+# For backwards compatibility, we keep it here (at least on 3.10 and 3.11).
|
||||||
|
+_INSTALL_SCHEMES['rpm_prefix'] = _INSTALL_SCHEMES['posix_prefix']
|
||||||
|
+
|
||||||
|
|
||||||
|
def _safe_realpath(path):
|
||||||
|
try:
|
||||||
|
@@ -211,11 +234,39 @@ def _extend_dict(target_dict, other_dict
|
||||||
|
target_dict[key] = value
|
||||||
|
|
||||||
|
|
||||||
|
+_CONFIG_VARS_LOCAL = None
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _config_vars_local():
|
||||||
|
+ # This function returns the config vars with prefixes amended to /usr/local
|
||||||
|
+ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
+ global _CONFIG_VARS_LOCAL
|
||||||
|
+ if _CONFIG_VARS_LOCAL is None:
|
||||||
|
+ _CONFIG_VARS_LOCAL = dict(get_config_vars())
|
||||||
|
+ _CONFIG_VARS_LOCAL['base'] = '/usr/local'
|
||||||
|
+ _CONFIG_VARS_LOCAL['platbase'] = '/usr/local'
|
||||||
|
+ return _CONFIG_VARS_LOCAL
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def _expand_vars(scheme, vars):
|
||||||
|
res = {}
|
||||||
|
if vars is None:
|
||||||
|
vars = {}
|
||||||
|
- _extend_dict(vars, get_config_vars())
|
||||||
|
+
|
||||||
|
+ # when we are not in a virtual environment or an RPM build
|
||||||
|
+ # we change '/usr' to '/usr/local'
|
||||||
|
+ # to avoid surprises, we explicitly check for the /usr/ prefix
|
||||||
|
+ # Python virtual environments have different prefixes
|
||||||
|
+ # we only do this for posix_prefix, not to mangle the venv scheme
|
||||||
|
+ # posix_prefix is used by sudo pip install
|
||||||
|
+ # we only change the defaults here, so explicit --prefix will take precedence
|
||||||
|
+ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
+ if (scheme == 'posix_prefix' and
|
||||||
|
+ _PREFIX == '/usr' and
|
||||||
|
+ 'RPM_BUILD_ROOT' not in os.environ):
|
||||||
|
+ _extend_dict(vars, _config_vars_local())
|
||||||
|
+ else:
|
||||||
|
+ _extend_dict(vars, get_config_vars())
|
||||||
|
|
||||||
|
for key, value in _INSTALL_SCHEMES[scheme].items():
|
||||||
|
if os.name in ('posix', 'nt'):
|
||||||
|
Index: Python-3.10.14/Lib/test/test_sysconfig.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.10.14.orig/Lib/test/test_sysconfig.py
|
||||||
|
+++ Python-3.10.14/Lib/test/test_sysconfig.py
|
||||||
|
@@ -105,8 +105,19 @@ class TestSysConfig(unittest.TestCase):
|
||||||
|
for scheme in _INSTALL_SCHEMES:
|
||||||
|
for name in _INSTALL_SCHEMES[scheme]:
|
||||||
|
expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars)
|
||||||
|
+ tested = get_path(name, scheme)
|
||||||
|
+ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
+ if tested.startswith('/usr/local'):
|
||||||
|
+ # /usr/local should only be used in posix_prefix
|
||||||
|
+ self.assertEqual(scheme, 'posix_prefix')
|
||||||
|
+ # Fedora CI runs tests for venv and virtualenv that check for other prefixes
|
||||||
|
+ self.assertEqual(sys.prefix, '/usr')
|
||||||
|
+ # When building the RPM of Python, %check runs this with RPM_BUILD_ROOT set
|
||||||
|
+ # Fedora CI runs this with RPM_BUILD_ROOT unset
|
||||||
|
+ self.assertNotIn('RPM_BUILD_ROOT', os.environ)
|
||||||
|
+ tested = tested.replace('/usr/local', '/usr')
|
||||||
|
self.assertEqual(
|
||||||
|
- os.path.normpath(get_path(name, scheme)),
|
||||||
|
+ os.path.normpath(tested),
|
||||||
|
os.path.normpath(expected),
|
||||||
|
)
|
||||||
|
|
||||||
|
@@ -263,7 +274,7 @@ class TestSysConfig(unittest.TestCase):
|
||||||
|
self.assertTrue(os.path.isfile(config_h), config_h)
|
||||||
|
|
||||||
|
def test_get_scheme_names(self):
|
||||||
|
- wanted = ['nt', 'posix_home', 'posix_prefix']
|
||||||
|
+ wanted = ['nt', 'posix_home', 'posix_prefix', 'rpm_prefix']
|
||||||
|
if HAS_USER_BASE:
|
||||||
|
wanted.extend(['nt_user', 'osx_framework_user', 'posix_user'])
|
||||||
|
self.assertEqual(get_scheme_names(), tuple(sorted(wanted)))
|
||||||
|
@@ -274,6 +285,8 @@ class TestSysConfig(unittest.TestCase):
|
||||||
|
cmd = "-c", "import sysconfig; print(sysconfig.get_platform())"
|
||||||
|
self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
|
||||||
|
|
||||||
|
+ @unittest.skipIf('RPM_BUILD_ROOT' not in os.environ,
|
||||||
|
+ "Test doesn't expect Fedora's paths")
|
||||||
|
def test_user_similar(self):
|
||||||
|
# Issue #8759: make sure the posix scheme for the users
|
||||||
|
# is similar to the global posix_prefix one
|
||||||
|
BIN
bluez-devel-vendor.tar.xz
(Stored with Git LFS)
BIN
bluez-devel-vendor.tar.xz
(Stored with Git LFS)
Binary file not shown.
37
bso1227999-reproducible-builds.patch
Normal file
37
bso1227999-reproducible-builds.patch
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
From ac2b8869724d7a57d9b5efbdce2f20423214e8bb Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Bernhard M. Wiedemann" <bwiedemann@suse.de>
|
||||||
|
Date: Tue, 16 Jul 2024 21:39:33 +0200
|
||||||
|
Subject: [PATCH] Allow to override build date with SOURCE_DATE_EPOCH
|
||||||
|
|
||||||
|
to make builds reproducible.
|
||||||
|
See https://reproducible-builds.org/ for why this is good
|
||||||
|
and https://reproducible-builds.org/specs/source-date-epoch/
|
||||||
|
for the definition of this variable.
|
||||||
|
---
|
||||||
|
Doc/conf.py | 3 ++-
|
||||||
|
Doc/library/functions.rst | 2 +-
|
||||||
|
2 files changed, 3 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
--- a/Doc/conf.py
|
||||||
|
+++ b/Doc/conf.py
|
||||||
|
@@ -89,7 +89,8 @@ html_short_title = '%s Documentation' %
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
|
# using the given strftime format.
|
||||||
|
-html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
+html_time = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
|
||||||
|
+html_last_updated_fmt = time.strftime('%b %d, %Y (%H:%M UTC)', time.gmtime(html_time))
|
||||||
|
|
||||||
|
# Path to find HTML templates.
|
||||||
|
templates_path = ['tools/templates']
|
||||||
|
--- a/Doc/library/functions.rst
|
||||||
|
+++ b/Doc/library/functions.rst
|
||||||
|
@@ -1320,7 +1320,7 @@ are always available. They are listed h
|
||||||
|
(where :func:`open` is declared), :mod:`os`, :mod:`os.path`, :mod:`tempfile`,
|
||||||
|
and :mod:`shutil`.
|
||||||
|
|
||||||
|
- .. audit-event:: open file,mode,flags open
|
||||||
|
+ .. audit-event:: open path,mode,flags open
|
||||||
|
|
||||||
|
The ``mode`` and ``flags`` arguments may have been modified or inferred from
|
||||||
|
the original call.
|
@ -1,3 +1,42 @@
|
|||||||
|
-------------------------------------------------------------------
|
||||||
|
Wed Aug 28 16:54:34 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||||
|
|
||||||
|
- Add CVE-2024-8088-inf-loop-zipfile_Path.patch to prevent
|
||||||
|
malformed payload to cause infinite loops in zipfile.Path
|
||||||
|
(bsc#1229704, CVE-2024-8088).
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Wed Aug 7 13:40:44 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||||
|
|
||||||
|
- Add CVE-2024-6923-email-hdr-inject.patch to prevent email
|
||||||
|
header injection due to unquoted newlines (bsc#1228780,
|
||||||
|
CVE-2024-6923).
|
||||||
|
- Adding bso1227999-reproducible-builds.patch fixing bsc#1227999
|
||||||
|
adding reproducibility patches from gh#python/cpython!121872
|
||||||
|
and gh#python/cpython!121883.
|
||||||
|
- %{profileopt} variable is set according to the variable
|
||||||
|
%{do_profiling} (bsc#1227999)
|
||||||
|
- Update bluez-devel-vendor.tar.xz
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Mon Jul 22 21:20:55 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||||
|
|
||||||
|
- Remove %suse_update_desktop_file macro as it is not useful any
|
||||||
|
more.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Mon Jul 15 12:14:57 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||||
|
|
||||||
|
- Stop using %%defattr, it seems to be breaking proper executable
|
||||||
|
attributes on /usr/bin/ scripts (bsc#1227378).
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue Jul 2 10:31:26 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com>
|
||||||
|
|
||||||
|
- Update F00251-change-user-install-location.patch to make pip and
|
||||||
|
modern tools install directly in /usr/local when used by the user.
|
||||||
|
bsc#1225660
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Tue Jun 25 21:57:40 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
Tue Jun 25 21:57:40 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||||
|
|
||||||
|
@ -36,6 +36,12 @@
|
|||||||
%bcond_without general
|
%bcond_without general
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if 0%{?do_profiling}
|
||||||
|
%bcond_without profileopt
|
||||||
|
%else
|
||||||
|
%bcond_with profileopt
|
||||||
|
%endif
|
||||||
|
|
||||||
%define python_pkg_name python310
|
%define python_pkg_name python310
|
||||||
%if "%{python_pkg_name}" == "%{primary_python}"
|
%if "%{python_pkg_name}" == "%{primary_python}"
|
||||||
%define primary_interpreter 1
|
%define primary_interpreter 1
|
||||||
@ -101,7 +107,6 @@ Obsoletes: python39%{?1:-%{1}}
|
|||||||
# pyexpat.cpython-35m-armv7-linux-gnueabihf
|
# pyexpat.cpython-35m-armv7-linux-gnueabihf
|
||||||
# _md5.cpython-38m-x86_64-linux-gnu.so
|
# _md5.cpython-38m-x86_64-linux-gnu.so
|
||||||
%define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so
|
%define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so
|
||||||
%bcond_without profileopt
|
|
||||||
Name: %{python_pkg_name}%{psuffix}
|
Name: %{python_pkg_name}%{psuffix}
|
||||||
Version: 3.10.14
|
Version: 3.10.14
|
||||||
Release: 0
|
Release: 0
|
||||||
@ -198,6 +203,15 @@ Patch22: CVE-2023-52425-libexpat-2.6.0-backport.patch
|
|||||||
# PATCH-FIX-UPSTREAM CVE-2024-4032-private-IP-addrs.patch bsc#1226448 mcepl@suse.com
|
# PATCH-FIX-UPSTREAM CVE-2024-4032-private-IP-addrs.patch bsc#1226448 mcepl@suse.com
|
||||||
# rearrange definition of private v global IP addresses
|
# rearrange definition of private v global IP addresses
|
||||||
Patch23: CVE-2024-4032-private-IP-addrs.patch
|
Patch23: CVE-2024-4032-private-IP-addrs.patch
|
||||||
|
# PATCH-FIX-UPSTREAM bso1227999-reproducible-builds.patch bsc#1227999 mcepl@suse.com
|
||||||
|
# reproducibility patches
|
||||||
|
Patch24: bso1227999-reproducible-builds.patch
|
||||||
|
# PATCH-FIX-UPSTREAM CVE-2024-6923-email-hdr-inject.patch bsc#1228780 mcepl@suse.com
|
||||||
|
# prevent email header injection, patch from gh#python/cpython!122608
|
||||||
|
Patch25: CVE-2024-6923-email-hdr-inject.patch
|
||||||
|
# PATCH-FIX-UPSTREAM CVE-2024-8088-inf-loop-zipfile_Path.patch bsc#1229704 mcepl@suse.com
|
||||||
|
# avoid denial of service in zipfile
|
||||||
|
Patch26: CVE-2024-8088-inf-loop-zipfile_Path.patch
|
||||||
BuildRequires: autoconf-archive
|
BuildRequires: autoconf-archive
|
||||||
BuildRequires: automake
|
BuildRequires: automake
|
||||||
BuildRequires: fdupes
|
BuildRequires: fdupes
|
||||||
@ -243,7 +257,6 @@ BuildRequires: gettext
|
|||||||
BuildRequires: readline-devel
|
BuildRequires: readline-devel
|
||||||
BuildRequires: sqlite-devel
|
BuildRequires: sqlite-devel
|
||||||
BuildRequires: timezone
|
BuildRequires: timezone
|
||||||
BuildRequires: update-desktop-files
|
|
||||||
BuildRequires: pkgconfig(ncurses)
|
BuildRequires: pkgconfig(ncurses)
|
||||||
BuildRequires: pkgconfig(tk)
|
BuildRequires: pkgconfig(tk)
|
||||||
BuildRequires: pkgconfig(x11)
|
BuildRequires: pkgconfig(x11)
|
||||||
@ -477,6 +490,9 @@ other applications.
|
|||||||
%patch -p1 -P 21
|
%patch -p1 -P 21
|
||||||
%patch -p1 -P 22
|
%patch -p1 -P 22
|
||||||
%patch -p1 -P 23
|
%patch -p1 -P 23
|
||||||
|
%patch -p1 -P 24
|
||||||
|
%patch -p1 -P 25
|
||||||
|
%patch -p1 -P 26
|
||||||
|
|
||||||
# drop Autoconf version requirement
|
# drop Autoconf version requirement
|
||||||
sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac
|
sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac
|
||||||
@ -708,7 +724,6 @@ done
|
|||||||
cp %{SOURCE19} idle%{python_version}.desktop
|
cp %{SOURCE19} idle%{python_version}.desktop
|
||||||
sed -i -e 's:idle3:idle%{python_version}:g' idle%{python_version}.desktop
|
sed -i -e 's:idle3:idle%{python_version}:g' idle%{python_version}.desktop
|
||||||
install -m 644 -D -t %{buildroot}%{_datadir}/applications idle%{python_version}.desktop
|
install -m 644 -D -t %{buildroot}%{_datadir}/applications idle%{python_version}.desktop
|
||||||
%suse_update_desktop_file idle%{python_version}
|
|
||||||
|
|
||||||
cp %{SOURCE20} idle%{python_version}.appdata.xml
|
cp %{SOURCE20} idle%{python_version}.appdata.xml
|
||||||
sed -i -e 's:idle3.desktop:idle%{python_version}.desktop:g' idle%{python_version}.appdata.xml
|
sed -i -e 's:idle3.desktop:idle%{python_version}.desktop:g' idle%{python_version}.appdata.xml
|
||||||
@ -816,25 +831,21 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
|
|
||||||
%if %{with general}
|
%if %{with general}
|
||||||
%files -n %{python_pkg_name}-tk
|
%files -n %{python_pkg_name}-tk
|
||||||
%defattr(644, root, root, 755)
|
|
||||||
%{sitedir}/tkinter
|
%{sitedir}/tkinter
|
||||||
%exclude %{sitedir}/tkinter/test
|
%exclude %{sitedir}/tkinter/test
|
||||||
%{dynlib _tkinter}
|
%{dynlib _tkinter}
|
||||||
|
|
||||||
%files -n %{python_pkg_name}-curses
|
%files -n %{python_pkg_name}-curses
|
||||||
%defattr(644, root, root, 755)
|
|
||||||
%{sitedir}/curses
|
%{sitedir}/curses
|
||||||
%{dynlib _curses}
|
%{dynlib _curses}
|
||||||
%{dynlib _curses_panel}
|
%{dynlib _curses_panel}
|
||||||
|
|
||||||
%files -n %{python_pkg_name}-dbm
|
%files -n %{python_pkg_name}-dbm
|
||||||
%defattr(644, root, root, 755)
|
|
||||||
%{sitedir}/dbm
|
%{sitedir}/dbm
|
||||||
%{dynlib _dbm}
|
%{dynlib _dbm}
|
||||||
%{dynlib _gdbm}
|
%{dynlib _gdbm}
|
||||||
|
|
||||||
%files -n %{python_pkg_name}
|
%files -n %{python_pkg_name}
|
||||||
%defattr(644, root, root, 755)
|
|
||||||
%dir %{sitedir}
|
%dir %{sitedir}
|
||||||
%dir %{sitedir}/lib-dynload
|
%dir %{sitedir}/lib-dynload
|
||||||
%{sitedir}/sqlite3
|
%{sitedir}/sqlite3
|
||||||
@ -846,7 +857,6 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
%files -n %{python_pkg_name}-idle
|
%files -n %{python_pkg_name}-idle
|
||||||
%defattr(644, root, root, 755)
|
|
||||||
%{sitedir}/idlelib
|
%{sitedir}/idlelib
|
||||||
%dir %{_sysconfdir}/idle%{python_version}
|
%dir %{_sysconfdir}/idle%{python_version}
|
||||||
%config %{_sysconfdir}/idle%{python_version}/*
|
%config %{_sysconfdir}/idle%{python_version}/*
|
||||||
@ -884,11 +894,9 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
%postun -n libpython%{so_version} -p /sbin/ldconfig
|
%postun -n libpython%{so_version} -p /sbin/ldconfig
|
||||||
|
|
||||||
%files -n libpython%{so_version}
|
%files -n libpython%{so_version}
|
||||||
%defattr(644, root,root)
|
|
||||||
%{_libdir}/libpython%{python_abi}.so.%{so_major}.%{so_minor}
|
%{_libdir}/libpython%{python_abi}.so.%{so_major}.%{so_minor}
|
||||||
|
|
||||||
%files -n %{python_pkg_name}-tools
|
%files -n %{python_pkg_name}-tools
|
||||||
%defattr(644, root, root, 755)
|
|
||||||
%{sitedir}/turtledemo
|
%{sitedir}/turtledemo
|
||||||
%if %{primary_interpreter}
|
%if %{primary_interpreter}
|
||||||
%{_bindir}/2to3
|
%{_bindir}/2to3
|
||||||
@ -897,7 +905,6 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
%doc %{_docdir}/%{name}/Tools
|
%doc %{_docdir}/%{name}/Tools
|
||||||
|
|
||||||
%files -n %{python_pkg_name}-devel
|
%files -n %{python_pkg_name}-devel
|
||||||
%defattr(644, root, root, 755)
|
|
||||||
%{_libdir}/libpython%{python_abi}.so
|
%{_libdir}/libpython%{python_abi}.so
|
||||||
%if %{primary_interpreter}
|
%if %{primary_interpreter}
|
||||||
%{_libdir}/libpython3.so
|
%{_libdir}/libpython3.so
|
||||||
@ -905,7 +912,6 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
%{_libdir}/pkgconfig/*
|
%{_libdir}/pkgconfig/*
|
||||||
%{_includedir}/python%{python_abi}
|
%{_includedir}/python%{python_abi}
|
||||||
%{sitedir}/config-%{python_abi}-*
|
%{sitedir}/config-%{python_abi}-*
|
||||||
%defattr(755, root, root)
|
|
||||||
%{_bindir}/python%{python_abi}-config
|
%{_bindir}/python%{python_abi}-config
|
||||||
%if %{primary_interpreter}
|
%if %{primary_interpreter}
|
||||||
%{_bindir}/python3-config
|
%{_bindir}/python3-config
|
||||||
@ -918,7 +924,6 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
%{_datadir}/gdb/auto-load/%{_libdir}/libpython%{python_abi}.so.%{so_major}.%{so_minor}-gdb.py
|
%{_datadir}/gdb/auto-load/%{_libdir}/libpython%{python_abi}.so.%{so_major}.%{so_minor}-gdb.py
|
||||||
|
|
||||||
%files -n %{python_pkg_name}-testsuite
|
%files -n %{python_pkg_name}-testsuite
|
||||||
%defattr(644, root, root, 755)
|
|
||||||
%{sitedir}/test
|
%{sitedir}/test
|
||||||
%{sitedir}/*/test
|
%{sitedir}/*/test
|
||||||
%{sitedir}/*/tests
|
%{sitedir}/*/tests
|
||||||
@ -935,7 +940,6 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
%dir %{sitedir}/tkinter
|
%dir %{sitedir}/tkinter
|
||||||
|
|
||||||
%files -n %{python_pkg_name}-base
|
%files -n %{python_pkg_name}-base
|
||||||
%defattr(644, root, root, 755)
|
|
||||||
# docs
|
# docs
|
||||||
%dir %{_docdir}/%{name}
|
%dir %{_docdir}/%{name}
|
||||||
%doc %{_docdir}/%{name}/README.rst
|
%doc %{_docdir}/%{name}/README.rst
|
||||||
|
Loading…
x
Reference in New Issue
Block a user