- Stop using %%defattr, it seems to be breaking proper executable

attributes on /usr/bin/ scripts (bsc#1227378).

OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:Factory/python?expand=0&rev=423
This commit is contained in:
Matej Cepl 2024-07-15 12:20:19 +00:00 committed by Git OBS Bridge
commit 9594a07523
76 changed files with 14319 additions and 0 deletions

23
.gitattributes vendored Normal file
View File

@ -0,0 +1,23 @@
## Default LFS
*.7z filter=lfs diff=lfs merge=lfs -text
*.bsp filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.gem filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.jar filter=lfs diff=lfs merge=lfs -text
*.lz filter=lfs diff=lfs merge=lfs -text
*.lzma filter=lfs diff=lfs merge=lfs -text
*.obscpio filter=lfs diff=lfs merge=lfs -text
*.oxt filter=lfs diff=lfs merge=lfs -text
*.pdf filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.rpm filter=lfs diff=lfs merge=lfs -text
*.tbz filter=lfs diff=lfs merge=lfs -text
*.tbz2 filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.txz filter=lfs diff=lfs merge=lfs -text
*.whl filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.osc

View File

@ -0,0 +1,98 @@
---
Doc/library/mailcap.rst | 13 +++++++++++++
Lib/mailcap.py | 28 ++++++++++++++++++++++++++--
2 files changed, 39 insertions(+), 2 deletions(-)
--- a/Doc/library/mailcap.rst
+++ b/Doc/library/mailcap.rst
@@ -55,6 +55,19 @@ standard. However, mailcap files are su
will automatically check such conditions and skip the entry if the check fails.
+.. versionchanged:: 3.11
+
+ To prevent security issues with shell metacharacters (symbols that have
+ special effects in a shell command line), ``findmatch`` will refuse
+ to inject ASCII characters other than alphanumerics and ``@+=:,./-_``
+ into the returned command line.
+
+ If a disallowed character appears in *filename*, ``findmatch`` will always
+ return ``(None, None)`` as if no entry was found.
+ If such a character appears elsewhere (a value in *plist* or in *MIMEtype*),
+ ``findmatch`` will ignore all mailcap entries which use that value.
+ A :mod:`warning <warnings>` will be raised in either case.
+
.. function:: getcaps()
Returns a dictionary mapping MIME types to a list of mailcap file entries. This
--- a/Lib/mailcap.py
+++ b/Lib/mailcap.py
@@ -1,9 +1,17 @@
"""Mailcap file handling. See RFC 1524."""
import os
+import warnings
+import re
__all__ = ["getcaps","findmatch"]
+_find_unsafe = re.compile(ur'[^\xa1-\U0010FFFF\w@+=:,./-]').search
+
+class UnsafeMailcapInput(Warning):
+ """Warning raised when refusing unsafe input"""
+
+
# Part 1: top-level interface.
def getcaps():
@@ -18,6 +26,10 @@ def getcaps():
"""
caps = {}
for mailcap in listmailcapfiles():
+ if _find_unsafe(mailcap):
+ msg = "Refusing to use mailcap with filename %r. Use a safe temporary filename." % (mailcap,)
+ warnings.warn(msg, UnsafeMailcapInput)
+ return None, None
try:
fp = open(mailcap, 'r')
except IOError:
@@ -149,10 +161,13 @@ def findmatch(caps, MIMEtype, key='view'
for e in entries:
if 'test' in e:
test = subst(e['test'], filename, plist)
+ if test is None:
+ continue
if test and os.system(test) != 0:
continue
command = subst(e[key], MIMEtype, filename, plist)
- return command, e
+ if command is not None:
+ return command, e
return None, None
def lookup(caps, MIMEtype, key=None):
@@ -184,6 +199,10 @@ def subst(field, MIMEtype, filename, pli
elif c == 's':
res = res + filename
elif c == 't':
+ if _find_unsafe(MIMEtype):
+ msg = "Refusing to substitute MIME type %r into a shell command." % (MIMEtype,)
+ warnings.warn(msg, UnsafeMailcapInput)
+ return None
res = res + MIMEtype
elif c == '{':
start = i
@@ -191,7 +210,12 @@ def subst(field, MIMEtype, filename, pli
i = i+1
name = field[start:i]
i = i+1
- res = res + findparam(name, plist)
+ param = findparam(name, plist)
+ if _find_unsafe(param):
+ msg = "Refusing to substitute parameter %r (%s) into a shell command" % (param, name)
+ warnings.warn(msg, UnsafeMailcapInput)
+ return None
+ res = res + param
# XXX To do:
# %n == number of parts if type is multipart/*
# %F == list of alternating type and filename for parts

22
CVE-2017-18207.patch Normal file
View File

@ -0,0 +1,22 @@
From ae0ed14794ced2c51c822fc6f0d3ca92064619dd Mon Sep 17 00:00:00 2001
From: BT123 <abcdyzhang@163.com>
Date: Fri, 17 Nov 2017 16:45:45 +0800
Subject: [PATCH] bug in wave.py
---
Lib/wave.py | 2 ++
1 file changed, 2 insertions(+)
Index: Python-2.7.13/Lib/wave.py
===================================================================
--- Python-2.7.13.orig/Lib/wave.py 2018-06-07 17:00:25.370728844 +0000
+++ Python-2.7.13/Lib/wave.py 2018-06-07 17:02:51.768202800 +0000
@@ -272,6 +272,8 @@ class Wave_read:
self._sampwidth = (sampwidth + 7) // 8
else:
raise Error, 'unknown format: %r' % (wFormatTag,)
+ if self._nchannels == 0:
+ raise Error, "The audio file in wav format should have at least one channel!"
self._framesize = self._nchannels * self._sampwidth
self._comptype = 'NONE'
self._compname = 'not compressed'

View File

@ -0,0 +1,42 @@
From 1fa6ef2bc7cee1c8e088dd8b397d9b2d54036dbc Mon Sep 17 00:00:00 2001
From: Rajarishi Devarajan <rishi93dev@gmail.com>
Date: Sun, 12 Jul 2020 23:47:42 +0200
Subject: [PATCH 1/4] bpo-39017 Fix infinite loop in the tarfile module
Add a check for length = 0 in the _proc_pax function to avoid running into an infinite loop
---
Lib/tarfile.py | 2 ++
Lib/test/test_tarfile.py | 5 +++++
Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst | 1 +
3 files changed, 8 insertions(+)
create mode 100644 Lib/test/recursion.tar
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -1400,6 +1400,8 @@ class TarInfo(object):
length, keyword = match.groups()
length = int(length)
+ if length == 0:
+ raise InvalidHeaderError("invalid header")
value = buf[match.end(2) + 1:match.start(1) + length - 1]
keyword = keyword.decode("utf8")
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -321,6 +321,11 @@ class CommonReadTest(ReadTest):
with self.assertRaisesRegexp(tarfile.ReadError, "unexpected end of data"):
tar.extractfile(t).read()
+ def test_length_zero_header(self):
+ # bpo-39017 (CVE-2019-20907): reading a zero-length header should fail
+ # with an exception
+ self.assertRaises(tarfile.ReadError, tarfile.open, test_support.findfile('recursion.tar'))
+
class MiscReadTest(CommonReadTest):
taropen = tarfile.TarFile.taropen
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst
@@ -0,0 +1 @@
+Avoid infinite loop when reading specially crafted TAR files using the tarfile module (CVE-2019-20907).

View File

@ -0,0 +1,58 @@
From 280917872027ee991416d2623fc16ff1eed48f50 Mon Sep 17 00:00:00 2001
From: Christian Heimes <christian@python.org>
Date: Tue, 15 Jan 2019 23:47:42 +0100
Subject: [PATCH] bpo-35746: Fix segfault in ssl's cert parser (GH-11569)
Fix a NULL pointer deref in ssl module. The cert parser did not handle CRL
distribution points with empty DP or URI correctly. A malicious or buggy
certificate can result into segfault.
Signed-off-by: Christian Heimes <christian@python.org>
https://bugs.python.org/issue35746
(cherry picked from commit a37f52436f9aa4b9292878b72f3ff1480e2606c3)
Co-authored-by: Christian Heimes <christian@python.org>
---
Lib/test/test_ssl.py | 21 ++++++++++
Misc/NEWS.d/next/Security/2019-01-15-18-16-05.bpo-35746.nMSd0j.rst | 3 +
2 files changed, 24 insertions(+)
create mode 100644 Lib/test/talos-2019-0758.pem
create mode 100644 Misc/NEWS.d/next/Security/2019-01-15-18-16-05.bpo-35746.nMSd0j.rst
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -287,6 +287,27 @@ class BasicSocketTests(unittest.TestCase
}
)
+ def test_parse_cert_CVE_2019_5010(self):
+ p = ssl._ssl._test_decode_cert(TALOS_INVALID_CRLDP)
+ if support.verbose:
+ sys.stdout.write("\n" + pprint.pformat(p) + "\n")
+ self.assertEqual(
+ p,
+ {
+ 'issuer': (
+ (('countryName', 'UK'),), (('commonName', 'cody-ca'),)),
+ 'notAfter': 'Jun 14 18:00:58 2028 GMT',
+ 'notBefore': 'Jun 18 18:00:58 2018 GMT',
+ 'serialNumber': '02',
+ 'subject': ((('countryName', 'UK'),),
+ (('commonName',
+ 'codenomicon-vm-2.test.lal.cisco.com'),)),
+ 'subjectAltName': (
+ ('DNS', 'codenomicon-vm-2.test.lal.cisco.com'),),
+ 'version': 3
+ }
+ )
+
def test_parse_cert_CVE_2013_4238(self):
p = ssl._ssl._test_decode_cert(NULLBYTECERT)
if support.verbose:
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2019-01-15-18-16-05.bpo-35746.nMSd0j.rst
@@ -0,0 +1,3 @@
+[CVE-2019-5010] Fix a NULL pointer deref in ssl module. The cert parser did
+not handle CRL distribution points with empty DP or URI correctly. A
+malicious or buggy certificate can result into segfault.

View File

@ -0,0 +1,69 @@
From b73fe12d4d85fc92e4b9658e417046b68fb68ecc Mon Sep 17 00:00:00 2001
From: nick sung <sungboss2004@gmail.com>
Date: Fri, 17 May 2019 15:45:31 +0800
Subject: [PATCH 1/4] bpo-36260: Add pitfalls to zipfile module documentation
We saw vulnerability warning description (including zip bomb) in Doc/library/xml.rst file.
This gave us the idea of documentation improvement.
So, we moved a little bit forward :P
And the doc patch can be found (pr).
---
Doc/library/zipfile.rst | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
--- a/Doc/library/zipfile.rst
+++ b/Doc/library/zipfile.rst
@@ -553,5 +553,47 @@ Command-line options
Test whether the zipfile is valid or not.
+Decompression pitfalls
+----------------------
+The extraction in zipfile module might fail due to some pitfalls
+listed below.
+
+From file itself
+~~~~~~~~~~~~~~~~
+
+Decompression may fail due to incorrect password / CRC checksum
+/ ZIP format or unsupported compression method / decryption.
+
+File System limitations
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Exceeding limitations on different file systems can cause
+decompression failed. Such as allowable characters in the
+directory entries, length of the file name, length of the
+pathname, size of a single file, and number of files, etc.
+
+Resources limitations
+~~~~~~~~~~~~~~~~~~~~~
+
+The lack of memory or disk volume would lead to decompression
+failed. For example, decompression bombs (aka `ZIP bomb`_) apply
+to zipfile library that can cause disk volume exhaustion.
+
+Interruption
+~~~~~~~~~~~~
+
+Interruption during the decompression, such as pressing control-C
+or killing the decompression process may result in incomplete
+decompression of the archive.
+
+Default behaviors of extraction
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Not knowing the default extraction behaviors can cause unexpected
+decompression results. For example, when extracting the same
+archive twice, it overwrites files without asking.
+
+
+.. _ZIP bomb: https://en.wikipedia.org/wiki/Zip_bomb
.. _PKZIP Application Note: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
--- /dev/null
+++ b/Misc/NEWS.d/next/Documentation/2019-06-04-09-29-00.bpo-36260.WrGuc-.rst
@@ -0,0 +1 @@
+Add decompression pitfalls to zipfile module documentation.
\ No newline at end of file

View File

@ -0,0 +1,77 @@
---
Lib/httplib.py | 15 +++++++++++++++
Lib/test/test_httplib.py | 22 +++++++++++++++++++++-
2 files changed, 36 insertions(+), 1 deletion(-)
--- a/Lib/httplib.py
+++ b/Lib/httplib.py
@@ -262,6 +262,10 @@ _contains_disallowed_url_pchar_re = re.c
_METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'}
+# These characters are not allowed within HTTP method names
+# to prevent http header injection.
+_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]')
+
class HTTPMessage(mimetools.Message):
def addheader(self, key, value):
@@ -940,6 +944,8 @@ class HTTPConnection:
else:
raise CannotSendRequest()
+ self._validate_method(method)
+
# Save the method for use later in the response phase
self._method = method
@@ -1179,6 +1185,15 @@ class HTTPConnection:
response.close()
raise
+ def _validate_method(self, method):
+ """Validate a method name for putrequest."""
+ # prevent http header injection
+ match = _contains_disallowed_method_pchar_re.search(method)
+ if match:
+ raise ValueError(
+ "method can't contain control characters. %r (found at "
+ "least %r)" % (method, match.group()))
+
class HTTP:
"Compatibility class with httplib.py from 1.5."
--- a/Lib/test/test_httplib.py
+++ b/Lib/test/test_httplib.py
@@ -1007,10 +1007,30 @@ class TunnelTests(TestCase):
self.assertTrue('Host: destination.com' in conn.sock.data)
+class HttpMethodTests(TestCase):
+ def test_invalid_method_names(self):
+ methods = (
+ 'GET\r',
+ 'POST\n',
+ 'PUT\n\r',
+ 'POST\nValue',
+ 'POST\nHOST:abc',
+ 'GET\nrHost:abc\n',
+ 'POST\rRemainder:\r',
+ 'GET\rHOST:\n',
+ '\nPUT'
+ )
+
+ for method in methods:
+ conn = httplib.HTTPConnection('example.com')
+ conn.sock = FakeSocket(None)
+ self.assertRaises(ValueError, conn.request, method=method, url="/")
+
+
@test_support.reap_threads
def test_main(verbose=None):
test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest,
- HTTPTest, HTTPSTest, SourceAddressTest,
+ HTTPTest, HttpMethodTests, HTTPSTest, SourceAddressTest,
TunnelTests)
if __name__ == '__main__':

View File

@ -0,0 +1,35 @@
From 34e25a97709a05f7c804036dd1e16afda6bdfa33 Mon Sep 17 00:00:00 2001
From: Victor Stinner <vstinner@python.org>
Date: Thu, 30 Jan 2020 16:13:03 +0100
Subject: [PATCH 1/2] bpo-39503: Fix urllib basic auth regex
The AbstractBasicAuthHandler class of the urllib.request module uses
an inefficient regular expression which can be exploited by an
attacker to cause a denial of service. Fix the regex to prevent the
catastrophic backtracking.
Vulnerability reported by Matt Schwager.
---
Lib/urllib2.py | 2 +-
Misc/NEWS.d/next/Security/2020-01-30-16-15-29.bpo-39503.B299Yq.rst | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Security/2020-01-30-16-15-29.bpo-39503.B299Yq.rst
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2020-01-30-16-15-29.bpo-39503.B299Yq.rst
@@ -0,0 +1,4 @@
+CVE-2020-8492: The :class:`~urllib.request.AbstractBasicAuthHandler` class of the
+:mod:`urllib.request` module uses an inefficient regular expression which can
+be exploited by an attacker to cause a denial of service. Fix the regex to
+prevent the catastrophic backtracking. Vulnerability reported by Matt Schwager.
--- a/Lib/urllib2.py
+++ b/Lib/urllib2.py
@@ -856,7 +856,7 @@ class AbstractBasicAuthHandler:
# allow for double- and single-quoted realm values
# (single quotes are a violation of the RFC, but appear in the wild)
- rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+'
+ rx = re.compile('(?:[^,]*,)*[ \t]*([^ \t]+)[ \t]+'
'realm=(["\']?)([^"\']*)\\2', re.I)
# XXX could pre-emptively send auth info already accepted (RFC 2617,

View File

@ -0,0 +1,389 @@
From 5c17dfc5d70ce88be99bc5769b91ce79d7a90d61 Mon Sep 17 00:00:00 2001
From: Senthil Kumaran <senthil@uthcode.com>
Date: Mon, 15 Feb 2021 11:16:43 -0800
Subject: [PATCH] [3.6] bpo-42967: only use '&' as a query string separator
(GH-24297) (GH-24532)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
bpo-42967: [security] Address a web cache-poisoning issue reported in
urllib.parse.parse_qsl().
urllib.parse will only us "&" as query string separator by default
instead of both ";" and "&" as allowed in earlier versions. An optional
argument seperator with default value "&" is added to specify the
separator.
Co-authored-by: Éric Araujo <merwok@netwok.org>
Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Co-authored-by: Adam Goldschmidt <adamgold7@gmail.com>
---
Doc/library/cgi.rst | 8 ++-
Doc/library/urllib.parse.rst | 22 +++++-
Doc/whatsnew/3.6.rst | 13 ++++
Lib/cgi.py | 17 +++--
Lib/test/test_cgi.py | 29 ++++++--
Lib/test/test_urlparse.py | 68 +++++++++++++------
Lib/urllib/parse.py | 19 ++++--
.../2021-02-14-15-59-16.bpo-42967.YApqDS.rst | 1 +
8 files changed, 134 insertions(+), 43 deletions(-)
create mode 100644 Misc/NEWS.d/next/Security/2021-02-14-15-59-16.bpo-42967.YApqDS.rst
--- a/Doc/library/cgi.rst
+++ b/Doc/library/cgi.rst
@@ -287,10 +287,11 @@ algorithms implemented in this module in
.. function:: parse(fp[, environ[, keep_blank_values[, strict_parsing]]])
- Parse a query in the environment or from a file (the file defaults to
- ``sys.stdin`` and environment defaults to ``os.environ``). The *keep_blank_values* and *strict_parsing* parameters are
- passed to :func:`urlparse.parse_qs` unchanged.
-
+ Parse a query in the environment or from a file (the file
+ defaults to ``sys.stdin`` and environment defaults to
+ ``os.environ``). The *keep_blank_values*, *strict_parsing*,
+ and *separator* parameters are passed to
+ :func:`urlparse.parse_qs` unchanged.
.. function:: parse_qs(qs[, keep_blank_values[, strict_parsing[, max_num_fields]]])
@@ -316,6 +317,9 @@ algorithms implemented in this module in
Note that this does not parse nested multipart parts --- use
:class:`FieldStorage` for that.
+ .. versionchanged:: 3.6.13
+ Added the *separator* parameter.
+
.. function:: parse_header(string)
--- a/Lib/cgi.py
+++ b/Lib/cgi.py
@@ -121,7 +121,8 @@ log = initlog # The current lo
# 0 ==> unlimited input
maxlen = 0
-def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
+def parse(fp=None, environ=os.environ, keep_blank_values=0,
+ strict_parsing=0, separator='&'):
"""Parse a query in the environment or from a file (default stdin)
Arguments, all optional:
@@ -140,6 +141,9 @@ def parse(fp=None, environ=os.environ, k
strict_parsing: flag indicating what to do with parsing errors.
If false (the default), errors are silently ignored.
If true, errors raise a ValueError exception.
+
+ separator: str. The symbol to use for separating the query arguments.
+ Defaults to &.
"""
if fp is None:
fp = sys.stdin
@@ -171,7 +175,8 @@ def parse(fp=None, environ=os.environ, k
else:
qs = ""
environ['QUERY_STRING'] = qs # XXX Shouldn't, really
- return urlparse.parse_qs(qs, keep_blank_values, strict_parsing)
+ return urlparse.parse_qs(qs, keep_blank_values, strict_parsing,
+ separator=separator)
# parse query string function called from urlparse,
@@ -395,7 +400,7 @@ class FieldStorage:
def __init__(self, fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0,
- max_num_fields=None):
+ max_num_fields=None, separator='&'):
"""Constructor. Read multipart/* until last part.
Arguments, all optional:
@@ -430,6 +435,7 @@ class FieldStorage:
self.keep_blank_values = keep_blank_values
self.strict_parsing = strict_parsing
self.max_num_fields = max_num_fields
+ self.separator = separator
if 'REQUEST_METHOD' in environ:
method = environ['REQUEST_METHOD'].upper()
self.qs_on_post = None
@@ -613,7 +619,9 @@ class FieldStorage:
if self.qs_on_post:
qs += '&' + self.qs_on_post
query = urlparse.parse_qsl(qs, self.keep_blank_values,
- self.strict_parsing, self.max_num_fields)
+ self.strict_parsing,
+ self.max_num_fields,
+ separator=self.separator)
self.list = [MiniFieldStorage(key, value) for key, value in query]
self.skip_lines()
@@ -629,7 +637,8 @@ class FieldStorage:
query = urlparse.parse_qsl(self.qs_on_post,
self.keep_blank_values,
self.strict_parsing,
- self.max_num_fields)
+ self.max_num_fields,
+ self.separator)
self.list.extend(MiniFieldStorage(key, value)
for key, value in query)
FieldStorageClass = None
@@ -642,7 +651,8 @@ class FieldStorage:
klass = self.FieldStorageClass or self.__class__
part = klass(self.fp, {}, ib,
environ, keep_blank_values, strict_parsing,
- max_num_fields)
+ max_num_fields,
+ self.separator)
# Throw first part away
while not part.done:
--- a/Lib/test/test_cgi.py
+++ b/Lib/test/test_cgi.py
@@ -61,12 +61,9 @@ parse_strict_test_cases = [
("", ValueError("bad query field: ''")),
("&", ValueError("bad query field: ''")),
("&&", ValueError("bad query field: ''")),
- (";", ValueError("bad query field: ''")),
- (";&;", ValueError("bad query field: ''")),
# Should the next few really be valid?
("=", {}),
("=&=", {}),
- ("=;=", {}),
# This rest seem to make sense
("=a", {'': ['a']}),
("&=a", ValueError("bad query field: ''")),
@@ -81,8 +78,6 @@ parse_strict_test_cases = [
("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}),
("a=a+b&a=b+a", {'a': ['a b', 'b a']}),
("x=1&y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
- ("x=1;y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
- ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env",
{'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'],
'cuyer': ['r'],
@@ -188,6 +183,30 @@ class CgiTests(unittest.TestCase):
self.assertEqual(expect[k], v)
self.assertItemsEqual(expect.values(), d.values())
+ def test_separator(self):
+ parse_semicolon = [
+ ("x=1;y=2.0", {'x': ['1'], 'y': ['2.0']}),
+ ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
+ (";", ValueError("bad query field: ''")),
+ (";;", ValueError("bad query field: ''")),
+ ("=;a", ValueError("bad query field: 'a'")),
+ (";b=a", ValueError("bad query field: ''")),
+ ("b;=a", ValueError("bad query field: 'b'")),
+ ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}),
+ ("a=a+b;a=b+a", {'a': ['a b', 'b a']}),
+ ]
+ for orig, expect in parse_semicolon:
+ env = {'QUERY_STRING': orig}
+ fs = cgi.FieldStorage(separator=';', environ=env)
+ if isinstance(expect, dict):
+ for key in expect.keys():
+ expect_val = expect[key]
+ self.assertIn(key, fs)
+ if len(expect_val) > 1:
+ self.assertEqual(fs.getvalue(key), expect_val)
+ else:
+ self.assertEqual(fs.getvalue(key), expect_val[0])
+
def test_log(self):
cgi.log("Testing")
--- a/Lib/test/test_urlparse.py
+++ b/Lib/test/test_urlparse.py
@@ -24,16 +24,10 @@ parse_qsl_test_cases = [
("&a=b", [('a', 'b')]),
("a=a+b&b=b+c", [('a', 'a b'), ('b', 'b c')]),
("a=1&a=2", [('a', '1'), ('a', '2')]),
- (";", []),
- (";;", []),
- (";a=b", [('a', 'b')]),
- ("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]),
- ("a=1;a=2", [('a', '1'), ('a', '2')]),
- (b";", []),
- (b";;", []),
- (b";a=b", [(b'a', b'b')]),
- (b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]),
- (b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]),
+ (";a=b", [(';a', 'b')]),
+ ("a=a+b;b=b+c", [('a', 'a b;b=b c')]),
+ (b";a=b", [(b';a', b'b')]),
+ (b"a=a+b;b=b+c", [(b'a', b'a b;b=b c')]),
]
parse_qs_test_cases = [
@@ -57,16 +51,10 @@ parse_qs_test_cases = [
(b"&a=b", {b'a': [b'b']}),
(b"a=a+b&b=b+c", {b'a': [b'a b'], b'b': [b'b c']}),
(b"a=1&a=2", {b'a': [b'1', b'2']}),
- (";", {}),
- (";;", {}),
- (";a=b", {'a': ['b']}),
- ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}),
- ("a=1;a=2", {'a': ['1', '2']}),
- (b";", {}),
- (b";;", {}),
- (b";a=b", {b'a': [b'b']}),
- (b"a=a+b;b=b+c", {b'a': [b'a b'], b'b': [b'b c']}),
- (b"a=1;a=2", {b'a': [b'1', b'2']}),
+ (";a=b", {';a': ['b']}),
+ ("a=a+b;b=b+c", {'a': ['a b;b=b c']}),
+ (b";a=b", {b';a': [b'b']}),
+ (b"a=a+b;b=b+c", {b'a':[ b'a b;b=b c']}),
]
class UrlParseTestCase(unittest.TestCase):
@@ -665,6 +653,43 @@ class UrlParseTestCase(unittest.TestCase
"under NFKC normalization")
self.assertIsInstance(cm.exception.args[0], str)
+ def test_parse_qs_separator(self):
+ parse_qs_semicolon_cases = [
+ (";", {}),
+ (";;", {}),
+ (";a=b", {'a': ['b']}),
+ ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}),
+ ("a=1;a=2", {'a': ['1', '2']}),
+ (b";", {}),
+ (b";;", {}),
+ (b";a=b", {b'a': [b'b']}),
+ (b"a=a+b;b=b+c", {b'a': [b'a b'], b'b': [b'b c']}),
+ (b"a=1;a=2", {b'a': [b'1', b'2']}),
+ ]
+ for orig, expect in parse_qs_semicolon_cases:
+ result = urlparse.parse_qs(orig, separator=';')
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+
+
+ def test_parse_qsl_separator(self):
+ parse_qsl_semicolon_cases = [
+ (";", []),
+ (";;", []),
+ (";a=b", [('a', 'b')]),
+ ("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]),
+ ("a=1;a=2", [('a', '1'), ('a', '2')]),
+ (b";", []),
+ (b";;", []),
+ (b";a=b", [(b'a', b'b')]),
+ (b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]),
+ (b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]),
+ ]
+ for orig, expect in parse_qsl_semicolon_cases:
+ result = urlparse.parse_qsl(orig, separator=';')
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+
+
+
def test_main():
test_support.run_unittest(UrlParseTestCase)
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2021-02-14-15-59-16.bpo-42967.YApqDS.rst
@@ -0,0 +1 @@
+Fix web cache poisoning vulnerability by defaulting the query args separator to ``&``, and allowing the user to choose a custom separator.
--- a/Lib/test/test_urllib2.py
+++ b/Lib/test/test_urllib2.py
@@ -1331,7 +1331,7 @@ class MiscTests(unittest.TestCase, FakeH
# level 'def urlopen()' function defined in this... (quite ugly)
# test suite. They use different url opening codepaths. Plain
# urlopen uses FancyURLOpener which goes via a codepath that
- # calls urllib.parse.quote() on the URL which makes all of the
+ # calls urlparse.quote() on the URL which makes all of the
# above attempts at injection within the url _path_ safe.
escaped_char_repr = repr(char).replace('\\', r'\\')
InvalidURL = httplib.InvalidURL
@@ -1354,7 +1354,7 @@ class MiscTests(unittest.TestCase, FakeH
# level 'def urlopen()' function defined in this... (quite ugly)
# test suite. They use different url opening codepaths. Plain
# urlopen uses FancyURLOpener which goes via a codepath that
- # calls urllib.parse.quote() on the URL which makes all of the
+ # calls urlparse.quote() on the URL which makes all of the
# above attempts at injection within the url _path_ safe.
InvalidURL = httplib.InvalidURL
with self.assertRaisesRegexp(InvalidURL,
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -4246,7 +4246,7 @@ Library
- bpo-18167: cgi.FieldStorage no longer fails to handle multipart/form-data
when \r\n appears at end of 65535 bytes without other newlines.
-- bpo-17403: urllib.parse.robotparser normalizes the urls before adding to
+- bpo-17403: urlparse.robotparser normalizes the urls before adding to
ruleline. This helps in handling certain types invalid urls in a
conservative manner. Patch contributed by Mher Movsisyan.
@@ -8271,7 +8271,7 @@ Core and Builtins
Library
-------
-- bpo-7904: Changes to urllib.parse.urlsplit to handle schemes as defined by
+- bpo-7904: Changes to urlparse.urlsplit to handle schemes as defined by
RFC3986. Anything before :// is considered a scheme and is followed by an
authority (or netloc) and by '/' led path, which is optional.
--- a/Lib/urlparse.py
+++ b/Lib/urlparse.py
@@ -382,7 +382,8 @@ def unquote(s):
append(item)
return ''.join(res)
-def parse_qs(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None):
+def parse_qs(qs, keep_blank_values=0, strict_parsing=0,
+ max_num_fields=None, separator='&'):
"""Parse a query given as a string argument.
Arguments:
@@ -402,17 +403,21 @@ def parse_qs(qs, keep_blank_values=0, st
max_num_fields: int. If set, then throws a ValueError if there
are more than n fields read by parse_qsl().
+
+ separator: str. The symbol to use for separating the query arguments.
+ Defaults to &.
"""
dict = {}
for name, value in parse_qsl(qs, keep_blank_values, strict_parsing,
- max_num_fields):
+ max_num_fields, separator=separator):
if name in dict:
dict[name].append(value)
else:
dict[name] = [value]
return dict
-def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None):
+def parse_qsl(qs, keep_blank_values=0, strict_parsing=0,
+ max_num_fields=None, separator='&'):
"""Parse a query given as a string argument.
Arguments:
@@ -432,17 +437,23 @@ def parse_qsl(qs, keep_blank_values=0, s
max_num_fields: int. If set, then throws a ValueError if there
are more than n fields read by parse_qsl().
+ separator: str. The symbol to use for separating the query arguments.
+ Defaults to &.
+
Returns a list, as G-d intended.
"""
# If max_num_fields is defined then check that the number of fields
# is less than max_num_fields. This prevents a memory exhaustion DOS
# attack via post bodies with many fields.
+ if not separator or (not isinstance(separator, (str, bytes))):
+ raise ValueError("Separator must be of type string or bytes.")
+
if max_num_fields is not None:
- num_fields = 1 + qs.count('&') + qs.count(';')
+ num_fields = 1 + qs.count(separator)
if max_num_fields < num_fields:
raise ValueError('Max number of fields exceeded')
- pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
+ pairs = [s1 for s1 in qs.split(separator)]
r = []
for name_value in pairs:
if not name_value and not strict_parsing:

View File

@ -0,0 +1,76 @@
Index: Python-2.7.18/Lib/BaseHTTPServer.py
===================================================================
--- Python-2.7.18.orig/Lib/BaseHTTPServer.py
+++ Python-2.7.18/Lib/BaseHTTPServer.py
@@ -287,6 +287,14 @@ class BaseHTTPRequestHandler(SocketServe
return False
self.command, self.path, self.request_version = command, path, version
+ # CVE-2021-28861: The purpose of replacing '//' with '/' is to
+ # protect against open redirect attacks possibly triggered if the
+ # path starts with '//' because http clients treat //path as an
+ # absolute URI without scheme (similar to http://path) rather than
+ # a path.
+ if self.path.startswith('//'):
+ self.path = '/' + self.path.lstrip('/') # Reduce to a single /
+
# Examine the headers and look for a Connection directive
self.headers = self.MessageClass(self.rfile, 0)
Index: Python-2.7.18/Lib/test/test_httpservers.py
===================================================================
--- Python-2.7.18.orig/Lib/test/test_httpservers.py
+++ Python-2.7.18/Lib/test/test_httpservers.py
@@ -417,6 +417,52 @@ class SimpleHTTPServerTestCase(BaseTestC
self.assertEqual(response.getheader("Location"),
self.tempdir_name + "/?hi=1")
+ def test_get_dir_redirect_location_domain_injection_bug(self):
+ """Ensure //evil.co/..%2f../../X does not put //evil.co/ in Location.
+ //netloc/ in a Location header is a redirect to a new host.
+ https://github.com/python/cpython/issues/87389
+ This checks that a path resolving to a directory on our server cannot
+ resolve into a redirect to another server.
+ """
+ os.mkdir(os.path.join(self.tempdir, 'existing_directory'))
+ url = '/python.org/..%2f..%2f..%2f..%2f..%2f../%0a%0d/../' + self.tempdir_name + '/existing_directory'
+ expected_location = url + '/' # /python.org.../ single slash single prefix, trailing slash
+ # Canonicalizes to /tmp/tempdir_name/existing_directory which does
+ # exist and is a dir, triggering the 301 redirect logic.
+ response = self.request(url)
+ self.check_status_and_reason(response, 301)
+ location = response.getheader('Location')
+ self.assertEqual(location, expected_location, msg='non-attack failed!')
+
+ # //python.org... multi-slash prefix, no trailing slash
+ attack_url = '/' + url
+ response = self.request(attack_url)
+ self.check_status_and_reason(response, 301)
+ location = response.getheader('Location')
+ self.assertFalse(location.startswith('//'), msg=location)
+ self.assertEqual(location, expected_location,
+ msg='Expected Location header to start with a single / and '
+ 'end with a / as this is a directory redirect.')
+ # ///python.org... triple-slash prefix, no trailing slash
+ attack3_url = '//' + url
+ response = self.request(attack3_url)
+ self.check_status_and_reason(response, 301)
+ self.assertEqual(response.getheader('Location'), expected_location)
+
+ # If the second word in the http request (Request-URI for the http
+ # method) is a full URI, we don't worry about it, as that'll be parsed
+ # and reassembled as a full URI within BaseHTTPRequestHandler.send_head
+ # so no errant scheme-less //netloc//evil.co/ domain mixup can happen.
+ attack_scheme_netloc_2slash_url = 'https://pypi.org/' + url
+ expected_scheme_netloc_location = attack_scheme_netloc_2slash_url + '/'
+ response = self.request(attack_scheme_netloc_2slash_url)
+ self.check_status_and_reason(response, 301)
+ location = response.getheader('Location')
+ # We're just ensuring that the scheme and domain make it through, if
+ # there are or aren't multiple slashes at the start of the path that
+ # follows that isn't important in this Location: header.
+ self.assertTrue(location.startswith('https://pypi.org/'), msg=location)
+
cgi_file1 = """\
#!%s

View File

@ -0,0 +1,172 @@
From 34df10a9a16b38d54421eeeaf73ec89828563be7 Mon Sep 17 00:00:00 2001
From: Benjamin Peterson <benjamin@python.org>
Date: Mon, 18 Jan 2021 15:11:46 -0600
Subject: [PATCH] [3.6] closes bpo-42938: Replace snprintf with Python unicode
formatting in ctypes param reprs. (GH-24250)
(cherry picked from commit 916610ef90a0d0761f08747f7b0905541f0977c7)
Co-authored-by: Benjamin Peterson <benjamin@python.org>
---
Lib/ctypes/test/test_parameters.py | 43 +++++++++++++++
.../2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst | 2 +
Modules/_ctypes/callproc.c | 55 +++++++------------
3 files changed, 66 insertions(+), 34 deletions(-)
create mode 100644 Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst
--- a/Lib/ctypes/test/test_parameters.py
+++ b/Lib/ctypes/test/test_parameters.py
@@ -1,4 +1,6 @@
-import unittest, sys
+import platform
+import sys
+import unittest
from ctypes.test import need_symbol
import test.support
@@ -206,6 +208,50 @@ class SimpleTypesTestCase(unittest.TestC
with self.assertRaises(ZeroDivisionError):
WorseStruct().__setstate__({}, b'foo')
+ def test_parameter_repr(self):
+ from ctypes import (
+ c_bool,
+ c_char,
+ c_wchar,
+ c_byte,
+ c_ubyte,
+ c_short,
+ c_ushort,
+ c_int,
+ c_uint,
+ c_long,
+ c_ulong,
+ c_longlong,
+ c_ulonglong,
+ c_float,
+ c_double,
+ c_longdouble,
+ c_char_p,
+ c_wchar_p,
+ c_void_p,
+ )
+ self.assertRegexpMatches(repr(c_bool.from_param(True)), r"^<cparam '\?' at 0x[A-Fa-f0-9]+>$")
+ self.assertEqual(repr(c_char.from_param('a')), "<cparam 'c' ('a')>")
+ self.assertRegexpMatches(repr(c_wchar.from_param('a')), r"^<cparam 'u' at 0x[A-Fa-f0-9]+>$")
+ self.assertEqual(repr(c_byte.from_param(98)), "<cparam 'b' (98)>")
+ self.assertEqual(repr(c_ubyte.from_param(98)), "<cparam 'B' (98)>")
+ self.assertEqual(repr(c_short.from_param(511)), "<cparam 'h' (511)>")
+ self.assertEqual(repr(c_ushort.from_param(511)), "<cparam 'H' (511)>")
+ self.assertRegexpMatches(repr(c_int.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
+ self.assertRegexpMatches(repr(c_uint.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
+ self.assertRegexpMatches(repr(c_long.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
+ self.assertRegexpMatches(repr(c_ulong.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
+ if platform.architecture()[0].startswith("64"):
+ self.assertRegexpMatches(repr(c_longlong.from_param(20000)), r"^<cparam '[liq]' \(20000\)>$")
+ self.assertRegexpMatches(repr(c_ulonglong.from_param(20000)), r"^<cparam '[LIQ]' \(20000\)>$")
+ self.assertEqual(repr(c_float.from_param(1.5)), "<cparam 'f' (1.5)>")
+ self.assertEqual(repr(c_double.from_param(1.5)), "<cparam 'd' (1.5)>")
+ self.assertEqual(repr(c_double.from_param(1e300)), "<cparam 'd' (1e+300)>")
+ self.assertRegexpMatches(repr(c_longdouble.from_param(1.5)), r"^<cparam ('d' \(1.5\)|'g' at 0x[A-Fa-f0-9]+)>$")
+ self.assertRegexpMatches(repr(c_char_p.from_param(b'hihi')), "^<cparam 'z' \(0x[A-Fa-f0-9]+\)>$")
+ self.assertRegexpMatches(repr(c_wchar_p.from_param('hihi')), "^<cparam 'Z' \(0x[A-Fa-f0-9]+\)>$")
+ self.assertRegexpMatches(repr(c_void_p.from_param(0x12)), r"^<cparam 'P' \(0x0*12\)>$")
+
################################################################
if __name__ == '__main__':
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst
@@ -0,0 +1,2 @@
+Avoid static buffers when computing the repr of :class:`ctypes.c_double` and
+:class:`ctypes.c_longdouble` values.
--- a/Modules/_ctypes/callproc.c
+++ b/Modules/_ctypes/callproc.c
@@ -460,50 +460,44 @@ PyCArg_dealloc(PyCArgObject *self)
static PyObject *
PyCArg_repr(PyCArgObject *self)
{
- char buffer[256];
switch(self->tag) {
case 'b':
case 'B':
- sprintf(buffer, "<cparam '%c' (%d)>",
+ return PyUnicode_FromFormat("<cparam '%c' (%d)>",
self->tag, self->value.b);
- break;
case 'h':
case 'H':
- sprintf(buffer, "<cparam '%c' (%d)>",
+ return PyUnicode_FromFormat("<cparam '%c' (%d)>",
self->tag, self->value.h);
- break;
case 'i':
case 'I':
- sprintf(buffer, "<cparam '%c' (%d)>",
+ return PyUnicode_FromFormat("<cparam '%c' (%d)>",
self->tag, self->value.i);
- break;
case 'l':
case 'L':
- sprintf(buffer, "<cparam '%c' (%ld)>",
+ return PyUnicode_FromFormat("<cparam '%c' (%ld)>",
self->tag, self->value.l);
- break;
-#ifdef HAVE_LONG_LONG
+#if defined(HAVE_LONG_LONG) && defined(LLONG_MAX)
case 'q':
case 'Q':
- sprintf(buffer,
- "<cparam '%c' (%" PY_FORMAT_LONG_LONG "d)>",
+ return PyUnicode_FromFormat("<cparam '%c' (%lld)>",
self->tag, self->value.q);
- break;
#endif
case 'd':
- sprintf(buffer, "<cparam '%c' (%f)>",
- self->tag, self->value.d);
- break;
- case 'f':
- sprintf(buffer, "<cparam '%c' (%f)>",
- self->tag, self->value.f);
- break;
+ case 'f': {
+ PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d);
+ if (f == NULL) {
+ return NULL;
+ }
+ PyObject *result = PyUnicode_FromFormat("<cparam '%c' (%R)>", self->tag, f);
+ Py_DECREF(f);
+ return result;
+ }
case 'c':
- sprintf(buffer, "<cparam '%c' (%c)>",
- self->tag, self->value.c);
- break;
+ return PyUnicode_FromFormat("<cparam '%c' ('%c')>",
+ self->tag, self->value.c);
/* Hm, are these 'z' and 'Z' codes useful at all?
Shouldn't they be replaced by the functionality of c_string
@@ -512,16 +506,13 @@ PyCArg_repr(PyCArgObject *self)
case 'z':
case 'Z':
case 'P':
- sprintf(buffer, "<cparam '%c' (%p)>",
+ return PyUnicode_FromFormat("<cparam '%c' (%p)>",
self->tag, self->value.p);
- break;
default:
- sprintf(buffer, "<cparam '%c' at %p>",
- self->tag, self);
- break;
+ return PyUnicode_FromFormat("<cparam '%c' at %p>",
+ (unsigned char)self->tag, (void *)self);
}
- return PyString_FromString(buffer);
}
static PyMemberDef PyCArgType_members[] = {

View File

@ -0,0 +1,15 @@
--- a/Lib/urllib2.py
+++ b/Lib/urllib2.py
@@ -856,7 +856,7 @@ class AbstractBasicAuthHandler:
# allow for double- and single-quoted realm values
# (single quotes are a violation of the RFC, but appear in the wild)
- rx = re.compile('(?:[^,]*,)*[ \t]*([^ \t]+)[ \t]+'
+ rx = re.compile('(?:[^,]*,)*[ \t]*([^ \t,]+)[ \t]+'
'realm=(["\']?)([^"\']*)\\2', re.I)
# XXX could pre-emptively send auth info already accepted (RFC 2617,
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2021-01-31-05-28-14.bpo-43075.DoAXqO.rst
@@ -0,0 +1 @@
+Fix Regular Expression Denial of Service (ReDoS) vulnerability in :class:`urllib.request.AbstractBasicAuthHandler`. The ReDoS-vulnerable regex has quadratic worst-case complexity and it allows cause a denial of service when identifying crafted invalid RFCs. This ReDoS issue is on the client side and needs remote attackers to control the HTTP server.

View File

@ -0,0 +1,26 @@
--- a/Lib/httplib.py
+++ b/Lib/httplib.py
@@ -449,6 +449,7 @@ class HTTPResponse:
if status != CONTINUE:
break
# skip the header from the 100 response
+ header_count = 0
while True:
skip = self.fp.readline(_MAXLINE + 1)
if len(skip) > _MAXLINE:
@@ -458,6 +459,10 @@ class HTTPResponse:
break
if self.debuglevel > 0:
print "header:", skip
+ # CVE-2021-3737: Fix infinitely reading potential HTTP headers on a 100 Continue status response from the server
+ header_count += 1
+ if header_count > _MAXHEADERS:
+ raise HTTPException("got more than %d headers" % _MAXHEADERS)
self.status = status
self.reason = reason.strip()
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2021-05-05-17-37-04.bpo-44022.bS3XJ9.rst
@@ -0,0 +1,2 @@
+mod:`http.client` now avoids infinitely reading potential HTTP headers after a
+``100 Continue`` status response from the server.

View File

@ -0,0 +1,135 @@
commit 0ab152c6b5d95caa2dc1a30fa96e10258b5f188e
Author: Gregory P. Smith <greg@krypto.org>
Date: Mon Mar 15 11:39:31 2021 -0700
bpo-43285 Make ftplib not trust the PASV response. (GH-24838)
bpo-43285: Make ftplib not trust the PASV response.
The IPv4 address value returned from the server in response to the PASV command
should not be trusted. This prevents a malicious FTP server from using the
response to probe IPv4 address and port combinations on the client network.
Instead of using the returned address, we use the IP address we're
already connected to. This is the strategy other ftp clients adopted,
and matches the only strategy available for the modern IPv6 EPSV command
where the server response must return a port number and nothing else.
For the rare user who _wants_ this ugly behavior, set a `trust_server_pasv_ipv4_address`
attribute on your `ftplib.FTP` instance to True.
---
Doc/whatsnew/2.7.rst | 10 +++
Lib/ftplib.py | 11 +++-
Lib/test/test_ftplib.py | 27 +++++++++-
Misc/NEWS.d/next/Security/2021-03-13-03-48-14.bpo-43285.g-Hah3.rst | 8 ++
4 files changed, 53 insertions(+), 3 deletions(-)
--- a/Doc/whatsnew/2.7.rst
+++ b/Doc/whatsnew/2.7.rst
@@ -166,6 +166,16 @@ The ``unittest`` module also automatical
when running tests.
+Post-EOS fixes
+==============
+
+A security fix alters the :class:`ftplib.FTP` behavior to not trust the
+IPv4 address sent from the remote server when setting up a passive data
+channel. We reuse the ftp server IP address instead. For unusual code
+requiring the old behavior, set a ``trust_server_pasv_ipv4_address``
+attribute on your FTP instance to ``True``. (See :issue:`43285`)
+
+
Python 3.1 Features
=======================
--- a/Lib/ftplib.py
+++ b/Lib/ftplib.py
@@ -107,7 +107,9 @@ class FTP:
sock = None
file = None
welcome = None
- passiveserver = 1
+ passiveserver = True
+ # Disables https://bugs.python.org/issue43285 security if set to True.
+ trust_server_pasv_ipv4_address = False
# Initialization method (called by class instantiation).
# Initialize host to localhost, port to standard ftp port
@@ -310,8 +312,13 @@ class FTP:
return sock
def makepasv(self):
+ """Internal: Does the PASV or EPSV handshake -> (address, port)"""
if self.af == socket.AF_INET:
- host, port = parse227(self.sendcmd('PASV'))
+ untrusted_host, port = parse227(self.sendcmd('PASV'))
+ if self.trust_server_pasv_ipv4_address:
+ host = untrusted_host
+ else:
+ host = self.sock.getpeername()[0]
else:
host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
return host, port
--- a/Lib/test/test_ftplib.py
+++ b/Lib/test/test_ftplib.py
@@ -67,6 +67,10 @@ class DummyFTPHandler(asynchat.async_cha
self.rest = None
self.next_retr_data = RETR_DATA
self.push('220 welcome')
+ # We use this as the string IPv4 address to direct the client
+ # to in response to a PASV command. To test security behavior.
+ # https://bugs.python.org/issue43285/.
+ self.fake_pasv_server_ip = '252.253.254.255'
def collect_incoming_data(self, data):
self.in_buffer.append(data)
@@ -109,7 +113,8 @@ class DummyFTPHandler(asynchat.async_cha
sock.bind((self.socket.getsockname()[0], 0))
sock.listen(5)
sock.settimeout(10)
- ip, port = sock.getsockname()[:2]
+ port = sock.getsockname()[1]
+ ip = self.fake_pasv_server_ip
ip = ip.replace('.', ',')
p1, p2 = divmod(port, 256)
self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
@@ -577,6 +582,26 @@ class TestFTPClass(TestCase):
# IPv4 is in use, just make sure send_epsv has not been used
self.assertEqual(self.server.handler_instance.last_received_cmd, 'pasv')
+ def test_makepasv_issue43285_security_disabled(self):
+ """Test the opt-in to the old vulnerable behavior."""
+ self.client.trust_server_pasv_ipv4_address = True
+ bad_host, port = self.client.makepasv()
+ self.assertEqual(
+ bad_host, self.server.handler_instance.fake_pasv_server_ip)
+ # Opening and closing a connection keeps the dummy server happy
+ # instead of timing out on accept.
+ socket.create_connection((self.client.sock.getpeername()[0], port),
+ timeout=TIMEOUT).close()
+
+ def test_makepasv_issue43285_security_enabled_default(self):
+ self.assertFalse(self.client.trust_server_pasv_ipv4_address)
+ trusted_host, port = self.client.makepasv()
+ self.assertNotEqual(
+ trusted_host, self.server.handler_instance.fake_pasv_server_ip)
+ # Opening and closing a connection keeps the dummy server happy
+ # instead of timing out on accept.
+ socket.create_connection((trusted_host, port), timeout=TIMEOUT).close()
+
def test_line_too_long(self):
self.assertRaises(ftplib.Error, self.client.sendcmd,
'x' * self.client.maxline * 2)
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2021-03-13-03-48-14.bpo-43285.g-Hah3.rst
@@ -0,0 +1,8 @@
+:mod:`ftplib` no longer trusts the IP address value returned from the server
+in response to the PASV command by default. This prevents a malicious FTP
+server from using the response to probe IPv4 address and port combinations
+on the client network.
+
+Code that requires the former vulnerable behavior may set a
+``trust_server_pasv_ipv4_address`` attribute on their
+:class:`ftplib.FTP` instances to ``True`` to re-enable it.

View File

@ -0,0 +1,169 @@
---
Doc/library/urlparse.rst | 14 ++
Doc/whatsnew/2.7.rst | 7 +
Lib/test/test_urlparse.py | 49 ++++++++++
Lib/urlparse.py | 12 ++
Misc/NEWS.d/next/Security/2021-04-25-07-46-37.bpo-43882.Jpwx85.rst | 6 +
5 files changed, 88 insertions(+)
--- a/Doc/library/urlparse.rst
+++ b/Doc/library/urlparse.rst
@@ -248,6 +248,10 @@ The :mod:`urlparse` module defines the f
decomposed before parsing, or is not a Unicode string, no error will be
raised.
+ Following the `WHATWG spec`_ that updates RFC 3986, ASCII
+ newline ``\n``, ``\r`` and tab ``\t`` characters are stripped
+ from the URL.
+
.. versionadded:: 2.2
.. versionchanged:: 2.5
@@ -257,6 +261,10 @@ The :mod:`urlparse` module defines the f
Characters that affect netloc parsing under NFKC normalization will
now raise :exc:`ValueError`.
+ .. versionchanged:: 3.6.14
+ ASCII newline and tab characters are stripped from the URL.
+
+.. _WHATWG spec: https://url.spec.whatwg.org/#concept-basic-url-parser
.. function:: urlunsplit(parts)
@@ -308,6 +316,11 @@ The :mod:`urlparse` module defines the f
.. seealso::
+ `WHATWG`_ - URL Living standard
+ Working Group for the URL Standard that defines URLs,
+ domains, IP addresses, the application/x-www-form-urlencoded format,
+ and their API.
+
:rfc:`3986` - Uniform Resource Identifiers
This is the current standard (STD66). Any changes to urlparse module
should conform to this. Certain deviations could be observed, which are
@@ -332,6 +345,7 @@ The :mod:`urlparse` module defines the f
:rfc:`1738` - Uniform Resource Locators (URL)
This specifies the formal syntax and semantics of absolute URLs.
+.. _WHATWG: https://url.spec.whatwg.org/
.. _urlparse-result-object:
--- a/Doc/whatsnew/2.7.rst
+++ b/Doc/whatsnew/2.7.rst
@@ -175,6 +175,13 @@ channel. We reuse the ftp server IP add
requiring the old behavior, set a ``trust_server_pasv_ipv4_address``
attribute on your FTP instance to ``True``. (See :issue:`43285`)
+The presence of newline or tab characters in parts of a URL allows for some
+forms of attacks. Following the WHATWG specification that updates RFC 3986,
+ASCII newline ``\n``, ``\r`` and tab ``\t`` characters are stripped from the
+URL by the parser :func:`urlparse` preventing such attacks. The removal
+characters are controlled by a new module level variable
+``urlparse._UNSAFE_URL_BYTES_TO_REMOVE``. (See :issue:`43882`)
+
Python 3.1 Features
=======================
--- a/Lib/test/test_urlparse.py
+++ b/Lib/test/test_urlparse.py
@@ -492,6 +492,55 @@ class UrlParseTestCase(unittest.TestCase
p = urlparse.urlsplit(url)
self.assertEqual(p.port, None)
+ def test_urlsplit_remove_unsafe_bytes(self):
+ # Remove ASCII tabs and newlines from input, for http common case scenario.
+ url = "h\nttp://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
+ p = urlparse.urlsplit(url)
+ self.assertEqual(p.scheme, "http")
+ self.assertEqual(p.netloc, "www.python.org")
+ self.assertEqual(p.path, "/javascript:alert('msg')/")
+ self.assertEqual(p.query, "query=something")
+ self.assertEqual(p.fragment, "fragment")
+ self.assertEqual(p.username, None)
+ self.assertEqual(p.password, None)
+ self.assertEqual(p.hostname, "www.python.org")
+ self.assertEqual(p.port, None)
+ self.assertEqual(p.geturl(), "http://www.python.org/javascript:alert('msg')/?query=something#fragment")
+
+ # Remove ASCII tabs and newlines from input as bytes, for http common case scenario.
+ url = b"h\nttp://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
+ p = urlparse.urlsplit(url)
+ self.assertEqual(p.scheme, b"http")
+ self.assertEqual(p.netloc, b"www.python.org")
+ self.assertEqual(p.path, b"/javascript:alert('msg')/")
+ self.assertEqual(p.query, b"query=something")
+ self.assertEqual(p.fragment, b"fragment")
+ self.assertEqual(p.username, None)
+ self.assertEqual(p.password, None)
+ self.assertEqual(p.hostname, b"www.python.org")
+ self.assertEqual(p.port, None)
+ self.assertEqual(p.geturl(), b"http://www.python.org/javascript:alert('msg')/?query=something#fragment")
+
+ # any scheme
+ url = "x-new-scheme\t://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
+ p = urlparse.urlsplit(url)
+ self.assertEqual(p.geturl(), "x-new-scheme://www.python.org/javascript:alert('msg')/?query=something#fragment")
+
+ # Remove ASCII tabs and newlines from input as bytes, any scheme.
+ url = b"x-new-scheme\t://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
+ p = urlparse.urlsplit(url)
+ self.assertEqual(p.geturl(), b"x-new-scheme://www.python.org/javascript:alert('msg')/?query=something#fragment")
+
+ # Unsafe bytes is not returned from urlparse cache.
+ # scheme is stored after parsing, sending an scheme with unsafe bytes *will not* return an unsafe scheme
+ url = "https://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
+ scheme = "htt\nps"
+ for _ in range(2):
+ p = urlparse.urlsplit(url, scheme=scheme)
+ self.assertEqual(p.scheme, "https")
+ self.assertEqual(p.geturl(), "https://www.python.org/javascript:alert('msg')/?query=something#fragment")
+
+
def test_issue14072(self):
p1 = urlparse.urlsplit('tel:+31-641044153')
self.assertEqual(p1.scheme, 'tel')
--- a/Lib/urlparse.py
+++ b/Lib/urlparse.py
@@ -62,6 +62,9 @@ scheme_chars = ('abcdefghijklmnopqrstuvw
'0123456789'
'+-.')
+# Unsafe bytes to be removed per WHATWG spec
+_UNSAFE_URL_BYTES_TO_REMOVE = ['\t', '\r', '\n']
+
MAX_CACHE_SIZE = 20
_parse_cache = {}
@@ -184,12 +187,21 @@ def _checknetloc(netloc):
"under NFKC normalization"
% netloc)
+
+def _remove_unsafe_bytes_from_url(url):
+ for b in _UNSAFE_URL_BYTES_TO_REMOVE:
+ url = url.replace(b, "")
+ return url
+
+
def urlsplit(url, scheme='', allow_fragments=True):
"""Parse a URL into 5 components:
<scheme>://<netloc>/<path>?<query>#<fragment>
Return a 5-tuple: (scheme, netloc, path, query, fragment).
Note that we don't break the components up in smaller bits
(e.g. netloc is a single string) and we don't expand % escapes."""
+ url = _remove_unsafe_bytes_from_url(url)
+ scheme = _remove_unsafe_bytes_from_url(scheme)
allow_fragments = bool(allow_fragments)
key = url, scheme, allow_fragments, type(url), type(scheme)
cached = _parse_cache.get(key, None)
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2021-04-25-07-46-37.bpo-43882.Jpwx85.rst
@@ -0,0 +1,6 @@
+The presence of newline or tab characters in parts of a URL could allow
+some forms of attacks.
+
+Following the controlling specification for URLs defined by WHATWG
+:func:`urlparse` now removes ASCII newlines and tabs from URLs,
+preventing such attacks.

View File

@ -0,0 +1,88 @@
From fa792ddee55dc02c6392842c8194a464339f6f1b Mon Sep 17 00:00:00 2001
From: "Miss Islington (bot)"
<31488909+miss-islington@users.noreply.github.com>
Date: Mon, 7 Nov 2022 18:57:10 -0800
Subject: [PATCH] [3.11] gh-98433: Fix quadratic time idna decoding. (GH-99092)
(GH-99222)
There was an unnecessary quadratic loop in idna decoding. This restores
the behavior to linear.
(cherry picked from commit d315722564927c7202dd6e111dc79eaf14240b0d)
(cherry picked from commit a6f6c3a3d6f2b580f2d87885c9b8a9350ad7bf15)
Co-authored-by: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
---
Lib/encodings/idna.py | 32 ++++------
Lib/test/test_codecs.py | 6 +
Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst | 6 +
3 files changed, 27 insertions(+), 17 deletions(-)
create mode 100644 Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst
--- a/Lib/encodings/idna.py
+++ b/Lib/encodings/idna.py
@@ -39,23 +39,21 @@ def nameprep(label):
# Check bidi
RandAL = map(stringprep.in_table_d1, label)
- for c in RandAL:
- if c:
- # There is a RandAL char in the string. Must perform further
- # tests:
- # 1) The characters in section 5.8 MUST be prohibited.
- # This is table C.8, which was already checked
- # 2) If a string contains any RandALCat character, the string
- # MUST NOT contain any LCat character.
- if filter(stringprep.in_table_d2, label):
- raise UnicodeError("Violation of BIDI requirement 2")
-
- # 3) If a string contains any RandALCat character, a
- # RandALCat character MUST be the first character of the
- # string, and a RandALCat character MUST be the last
- # character of the string.
- if not RandAL[0] or not RandAL[-1]:
- raise UnicodeError("Violation of BIDI requirement 3")
+ if any(RandAL):
+ # There is a RandAL char in the string. Must perform further
+ # tests:
+ # 1) The characters in section 5.8 MUST be prohibited.
+ # This is table C.8, which was already checked
+ # 2) If a string contains any RandALCat character, the string
+ # MUST NOT contain any LCat character.
+ if any(stringprep.in_table_d2(x) for x in label):
+ raise UnicodeError("Violation of BIDI requirement 2")
+ # 3) If a string contains any RandALCat character, a
+ # RandALCat character MUST be the first character of the
+ # string, and a RandALCat character MUST be the last
+ # character of the string.
+ if not RandAL[0] or not RandAL[-1]:
+ raise UnicodeError("Violation of BIDI requirement 3")
return label
--- a/Lib/test/test_codecs.py
+++ b/Lib/test/test_codecs.py
@@ -1318,6 +1318,12 @@ class IDNACodecTest(unittest.TestCase):
self.assertEqual(u"pyth\xf6n.org".encode("idna"), "xn--pythn-mua.org")
self.assertEqual(u"pyth\xf6n.org.".encode("idna"), "xn--pythn-mua.org.")
+ def test_builtin_decode_length_limit(self):
+ with self.assertRaisesRegexp(UnicodeError, "too long"):
+ (b"xn--016c"+b"a"*1100).decode("idna")
+ with self.assertRaisesRegexp(UnicodeError, "too long"):
+ (b"xn--016c"+b"a"*70).decode("idna")
+
def test_stream(self):
import StringIO
r = codecs.getreader("idna")(StringIO.StringIO("abc"))
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst
@@ -0,0 +1,6 @@
+The IDNA codec decoder used on DNS hostnames by :mod:`socket` or :mod:`asyncio`
+related name resolution functions no longer involves a quadratic algorithm.
+This prevents a potential CPU denial of service if an out-of-spec excessive
+length hostname involving bidirectional characters were decoded. Some protocols
+such as :mod:`urllib` http ``3xx`` redirects potentially allow for an attacker
+to supply such a name.

View File

@ -0,0 +1,80 @@
From 4d8f9e2e4461de92bd1e0c92ed433480d761670f Mon Sep 17 00:00:00 2001
From: Ned Deily <nad@python.org>
Date: Mon, 19 Oct 2020 22:36:27 -0400
Subject: [PATCH] bpo-42051: Reject XML entity declarations in plist files
(GH-22760) (GH-22801)
Co-authored-by: Ronald Oussoren <ronaldoussoren@mac.com>
(cherry picked from commit e512bc799e3864fe3b1351757261762d63471efc)
Co-authored-by: Ned Deily <nad@python.org>
---
Lib/plistlib.py | 10 +++++
Lib/test/test_plistlib.py | 19 ++++++++++
Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst | 3 +
3 files changed, 32 insertions(+)
create mode 100644 Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst
--- a/Lib/plistlib.py
+++ b/Lib/plistlib.py
@@ -403,9 +403,19 @@ class PlistParser:
parser.StartElementHandler = self.handleBeginElement
parser.EndElementHandler = self.handleEndElement
parser.CharacterDataHandler = self.handleData
+ parser.EntityDeclHandler = self.handle_entity_decl
parser.ParseFile(fileobj)
return self.root
+ def handle_entity_decl(self, entity_name, is_parameter_entity, value,
+ base, system_id, public_id, notation_name):
+ # Reject plist files with entity declarations to avoid XML
+ # vulnerabilies in expat. Regular plist files don't contain
+ # those declerations, and Apple's plutil tool does not accept
+ # them either.
+ raise ValueError(
+ "XML entity declarations are not supported in plist files")
+
def handleBeginElement(self, element, attrs):
self.data = []
handler = getattr(self, "begin_" + element, None)
--- a/Lib/test/test_plistlib.py
+++ b/Lib/test/test_plistlib.py
@@ -86,6 +86,19 @@ TESTDATA = """<?xml version="1.0" encodi
</plist>
""".replace(" " * 8, "\t") # Apple as well as plistlib.py output hard tabs
+XML_PLIST_WITH_ENTITY=b'''\
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" [
+ <!ENTITY entity "replacement text">
+ ]>
+<plist version="1.0">
+ <dict>
+ <key>A</key>
+ <string>&entity;</string>
+ </dict>
+</plist>
+'''
+
class TestPlistlib(unittest.TestCase):
@@ -195,6 +208,12 @@ class TestPlistlib(unittest.TestCase):
self.assertEqual(test1, result1)
self.assertEqual(test2, result2)
+ def test_xml_plist_with_entity_decl(self):
+ with self.assertRaisesRegexp(ValueError,
+ "XML entity declarations are not supported"):
+ plistlib.readPlistFromString(XML_PLIST_WITH_ENTITY)
+
+
def test_main():
test_support.run_unittest(TestPlistlib)
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst
@@ -0,0 +1,3 @@
+The :mod:`plistlib` module no longer accepts entity declarations in XML
+plist files to avoid XML vulnerabilities. This should not affect users as
+entity declarations are not used in regular plist files.

View File

@ -0,0 +1,35 @@
From 8bef9ebb1b88cfa4b2a38b93fe4ea22015d8254a Mon Sep 17 00:00:00 2001
From: "Miss Islington (bot)"
<31488909+miss-islington@users.noreply.github.com>
Date: Mon, 14 Dec 2020 09:04:57 -0800
Subject: [PATCH] bpo-40791: Make compare_digest more constant-time. (GH-23438)
(GH-23767)
The existing volatile `left`/`right` pointers guarantee that the reads will all occur, but does not guarantee that they will be _used_. So a compiler can still short-circuit the loop, saving e.g. the overhead of doing the xors and especially the overhead of the data dependency between `result` and the reads. That would change performance depending on where the first unequal byte occurs. This change removes that optimization.
(This is change GH-1 from https://bugs.python.org/issue40791 .)
(cherry picked from commit 31729366e2bc09632e78f3896dbce0ae64914f28)
Co-authored-by: Devin Jeanpierre <jeanpierreda@google.com>
---
Misc/NEWS.d/next/Security/2020-05-28-06-06-47.bpo-40791.QGZClX.rst | 1 +
Modules/operator.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Security/2020-05-28-06-06-47.bpo-40791.QGZClX.rst
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2020-05-28-06-06-47.bpo-40791.QGZClX.rst
@@ -0,0 +1 @@
+Add ``volatile`` to the accumulator variable in ``hmac.compare_digest``, making constant-time-defeating optimizations less likely.
\ No newline at end of file
--- a/Modules/operator.c
+++ b/Modules/operator.c
@@ -259,7 +259,7 @@ _tscmp(const unsigned char *a, const uns
volatile const unsigned char *left;
volatile const unsigned char *right;
Py_ssize_t i;
- unsigned char result;
+ volatile unsigned char result;
/* loop count depends on length of b */
length = len_b;

View File

@ -0,0 +1,84 @@
---
Lib/test/test_urlparse.py | 21 ++++++++++
Lib/urlparse.py | 9 +++-
Misc/NEWS.d/next/Library/2022-11-12-15-45-51.gh-issue-99418.FxfAXS.rs | 2
3 files changed, 30 insertions(+), 2 deletions(-)
Index: Python-2.7.18/Lib/test/test_urlparse.py
===================================================================
--- Python-2.7.18.orig/Lib/test/test_urlparse.py
+++ Python-2.7.18/Lib/test/test_urlparse.py
@@ -1,4 +1,5 @@
from test import test_support
+from urlparse import isascii
import sys
import unicodedata
import unittest
@@ -592,6 +593,26 @@ class UrlParseTestCase(unittest.TestCase
self.assertEqual(p.netloc, "www.example.net:foo")
self.assertRaises(ValueError, lambda: p.port)
+ def do_attributes_bad_scheme(self, bytes, parse, scheme):
+ url = scheme + "://www.example.net"
+ if bytes:
+ if isascii(url):
+ url = url.encode("ascii")
+ else:
+ return
+ p = parse(url)
+ if bytes:
+ self.assertEqual(p.scheme, b"")
+ else:
+ self.assertEqual(p.scheme, "")
+
+ def test_attributes_bad_scheme(self):
+ """Check handling of invalid schemes."""
+ for bytes in (False, True):
+ for parse in (urlparse.urlsplit, urlparse.urlparse):
+ for scheme in (".", "+", "-", "0", "http&"):
+ self.do_attributes_bad_scheme(bytes, parse, scheme)
+
def test_attributes_without_netloc(self):
# This example is straight from RFC 3261. It looks like it
# should allow the username, hostname, and port to be filled
Index: Python-2.7.18/Lib/urlparse.py
===================================================================
--- Python-2.7.18.orig/Lib/urlparse.py
+++ Python-2.7.18/Lib/urlparse.py
@@ -31,7 +31,8 @@ test_urlparse.py provides a good indicat
import re
__all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag",
- "urlsplit", "urlunsplit", "parse_qs", "parse_qsl"]
+ "urlsplit", "urlunsplit", "parse_qs", "parse_qsl",
+ "isascii"]
# A classification of schemes ('' means apply by default)
uses_relative = ['ftp', 'http', 'gopher', 'nntp', 'imap',
@@ -68,6 +69,10 @@ _UNSAFE_URL_BYTES_TO_REMOVE = ['\t', '\r
MAX_CACHE_SIZE = 20
_parse_cache = {}
+# Py3k shim
+def isascii(word):
+ return all([ord(c) < 128 for c in word])
+
def clear_cache():
"""Clear the parse cache."""
_parse_cache.clear()
@@ -211,7 +216,7 @@ def urlsplit(url, scheme='', allow_fragm
clear_cache()
netloc = query = fragment = ''
i = url.find(':')
- if i > 0:
+ if i > 0 and isascii(url[0]) and url[0].isalpha():
if url[:i] == 'http': # optimize the common case
scheme = url[:i].lower()
url = url[i+1:]
Index: Python-2.7.18/Misc/NEWS.d/next/Library/2022-11-12-15-45-51.gh-issue-99418.FxfAXS.rs
===================================================================
--- /dev/null
+++ Python-2.7.18/Misc/NEWS.d/next/Library/2022-11-12-15-45-51.gh-issue-99418.FxfAXS.rs
@@ -0,0 +1,2 @@
+Fix bug in :func:`urllib.parse.urlparse` that causes URL schemes that begin
+with a digit, a plus sign, or a minus sign to be parsed incorrectly.

View File

@ -0,0 +1,528 @@
---
Doc/library/email.utils.rst | 19
Lib/email/test/test_email.py | 192 +++++++++-
Lib/email/test/test_email_renamed.py | 50 ++
Lib/email/utils.py | 155 +++++++-
Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8
5 files changed, 393 insertions(+), 31 deletions(-)
--- a/Doc/library/email.utils.rst
+++ b/Doc/library/email.utils.rst
@@ -21,13 +21,18 @@ There are several useful utilities provi
begins with angle brackets, they are stripped off.
-.. function:: parseaddr(address)
+.. function:: parseaddr(address, strict=True)
Parse address -- which should be the value of some address-containing field such
as :mailheader:`To` or :mailheader:`Cc` -- into its constituent *realname* and
*email address* parts. Returns a tuple of that information, unless the parse
fails, in which case a 2-tuple of ``('', '')`` is returned.
+ If *strict* is true, use a strict parser which rejects malformed inputs.
+
+ .. versionchanged:: 3.13
+ Add *strict* optional parameter and reject malformed inputs by default.
+
.. function:: formataddr(pair)
@@ -37,12 +42,15 @@ There are several useful utilities provi
second element is returned unmodified.
-.. function:: getaddresses(fieldvalues)
+.. function:: getaddresses(fieldvalues, strict=True)
This method returns a list of 2-tuples of the form returned by ``parseaddr()``.
*fieldvalues* is a sequence of header field values as might be returned by
- :meth:`Message.get_all <email.message.Message.get_all>`. Here's a simple
- example that gets all the recipients of a message::
+ :meth:`Message.get_all <email.message.Message.get_all>`.
+
+ If *strict* is true, use a strict parser which rejects malformed inputs.
+
+ Here's a simple example that gets all the recipients of a message::
from email.utils import getaddresses
@@ -52,6 +60,9 @@ There are several useful utilities provi
resent_ccs = msg.get_all('resent-cc', [])
all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)
+ .. versionchanged:: 3.13
+ Add *strict* optional parameter and reject malformed inputs by default.
+
.. function:: parsedate(date)
--- a/Lib/email/test/test_email.py
+++ b/Lib/email/test/test_email.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# Copyright (C) 2001-2010 Python Software Foundation
# Contact: email-sig@python.org
# email package unit tests
@@ -2414,15 +2415,142 @@ Foo
[('Al Person', 'aperson@dom.ain'),
('Bud Person', 'bperson@dom.ain')])
+ def test_parsing_errors(self):
+ """Test for parsing errors from CVE-2023-27043 and CVE-2019-16056"""
+ alice = 'alice@example.org'
+ bob = 'bob@example.com'
+ empty = ('', '')
+
+ # Test Utils.getaddresses() and Utils.parseaddr() on malformed email
+ # addresses: default behavior (strict=True) rejects malformed address,
+ # and strict=False which tolerates malformed address.
+ for invalid_separator, expected_non_strict in (
+ ('(', [('<%s>' % bob, alice)]),
+ (')', [('', alice), empty, ('', bob)]),
+ ('<', [('', alice), empty, ('', bob), empty]),
+ ('>', [('', alice), empty, ('', bob)]),
+ ('[', [('', '%s[<%s>]' % (alice, bob))]),
+ (']', [('', alice), empty, ('', bob)]),
+ ('@', [empty, empty, ('', bob)]),
+ (';', [('', alice), empty, ('', bob)]),
+ (':', [('', alice), ('', bob)]),
+ ('.', [('', alice + '.'), ('', bob)]),
+ ('"', [('', alice), ('', '<%s>' % bob)]),
+ ):
+ address = '%s%s<%s>' % (alice, invalid_separator, bob)
+ self.assertEqual(Utils.getaddresses([address]),
+ [empty])
+ self.assertEqual(Utils.getaddresses([address], strict=False),
+ expected_non_strict)
+
+ self.assertEqual(Utils.parseaddr([address]),
+ empty)
+ self.assertEqual(Utils.parseaddr([address], strict=False),
+ ('', address))
+
+ # Comma (',') is treated differently depending on strict parameter.
+ # Comma without quotes.
+ address = '%s,<%s>' % (alice, bob)
+ self.assertEqual(Utils.getaddresses([address]),
+ [('', alice), ('', bob)])
+ self.assertEqual(Utils.getaddresses([address], strict=False),
+ [('', alice), ('', bob)])
+ self.assertEqual(Utils.parseaddr([address]),
+ empty)
+ self.assertEqual(Utils.parseaddr([address], strict=False),
+ ('', address))
+
+ # Real name between quotes containing comma.
+ address = '"Alice, alice@example.org" <bob@example.com>'
+ expected_strict = ('Alice, alice@example.org', 'bob@example.com')
+ self.assertEqual(Utils.getaddresses([address]), [expected_strict])
+ self.assertEqual(Utils.getaddresses([address], strict=False), [expected_strict])
+ self.assertEqual(Utils.parseaddr([address]), expected_strict)
+ self.assertEqual(Utils.parseaddr([address], strict=False),
+ ('', address))
+
+ # Valid parenthesis in comments.
+ address = 'alice@example.org (Alice)'
+ expected_strict = ('Alice', 'alice@example.org')
+ self.assertEqual(Utils.getaddresses([address]), [expected_strict])
+ self.assertEqual(Utils.getaddresses([address], strict=False), [expected_strict])
+ self.assertEqual(Utils.parseaddr([address]), expected_strict)
+ self.assertEqual(Utils.parseaddr([address], strict=False),
+ ('', address))
+
+ # Invalid parenthesis in comments.
+ address = 'alice@example.org )Alice('
+ self.assertEqual(Utils.getaddresses([address]), [empty])
+ self.assertEqual(Utils.getaddresses([address], strict=False),
+ [('', 'alice@example.org'), ('', ''), ('', 'Alice')])
+ self.assertEqual(Utils.parseaddr([address]), empty)
+ self.assertEqual(Utils.parseaddr([address], strict=False),
+ ('', address))
+
+ # Two addresses with quotes separated by comma.
+ address = '"Jane Doe" <jane@example.net>, "John Doe" <john@example.net>'
+ self.assertEqual(Utils.getaddresses([address]),
+ [('Jane Doe', 'jane@example.net'),
+ ('John Doe', 'john@example.net')])
+ self.assertEqual(Utils.getaddresses([address], strict=False),
+ [('Jane Doe', 'jane@example.net'),
+ ('John Doe', 'john@example.net')])
+ self.assertEqual(Utils.parseaddr([address]), empty)
+ self.assertEqual(Utils.parseaddr([address], strict=False),
+ ('', address))
+
+ # Test Utils.supports_strict_parsing attribute
+ self.assertEqual(Utils.supports_strict_parsing, True)
+
+ def test_parsing_unicode_str(self):
+ email_in = "Honza Novák <honza@example.com>"
+ self.assertEqual(Utils.parseaddr("Honza str Novák <honza@example.com>"),
+ ('Honza str Nov\xc3\xa1k', 'honza@example.com'))
+ self.assertEqual(Utils.parseaddr(u"Honza unicode Novák <honza@example.com>"),
+ (u'Honza unicode Nov\xe1k', u'honza@example.com'))
+
def test_getaddresses_nasty(self):
- eq = self.assertEqual
- eq(Utils.getaddresses(['foo: ;']), [('', '')])
- eq(Utils.getaddresses(
- ['[]*-- =~$']),
- [('', ''), ('', ''), ('', '*--')])
- eq(Utils.getaddresses(
- ['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>']),
- [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')])
+ for addresses, expected in (
+ ([u'"Sürname, Firstname" <to@example.com>'],
+ [(u'Sürname, Firstname', 'to@example.com')]),
+
+ (['foo: ;'],
+ [('', '')]),
+
+ (['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>'],
+ [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]),
+
+ ([r'Pete(A nice \) chap) <pete(his account)@silly.test(his host)>'],
+ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]),
+
+ (['(Empty list)(start)Undisclosed recipients :(nobody(I know))'],
+ [('', '')]),
+
+ (['Mary <@machine.tld:mary@example.net>, , jdoe@test . example'],
+ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]),
+
+ (['John Doe <jdoe@machine(comment). example>'],
+ [('John Doe (comment)', 'jdoe@machine.example')]),
+
+ (['"Mary Smith: Personal Account" <smith@home.example>'],
+ [('Mary Smith: Personal Account', 'smith@home.example')]),
+
+ (['Undisclosed recipients:;'],
+ [('', '')]),
+
+ ([r'<boss@nil.test>, "Giant; \"Big\" Box" <bob@example.net>'],
+ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]),
+ ):
+ self.assertEqual(Utils.getaddresses(addresses),
+ expected)
+ self.assertEqual(Utils.getaddresses(addresses, strict=False),
+ expected)
+
+ addresses = ['[]*-- =~$']
+ self.assertEqual(Utils.getaddresses(addresses),
+ [('', '')])
+ self.assertEqual(Utils.getaddresses(addresses, strict=False),
+ [('', ''), ('', ''), ('', '*--')])
def test_getaddresses_embedded_comment(self):
"""Test proper handling of a nested comment"""
@@ -2430,6 +2558,54 @@ Foo
addrs = Utils.getaddresses(['User ((nested comment)) <foo@bar.com>'])
eq(addrs[0][1], 'foo@bar.com')
+ def test_iter_escaped_chars(self):
+ self.assertEqual(list(Utils._iter_escaped_chars(r'a\\b\"c\\"d')),
+ [(0, 'a'),
+ (2, '\\\\'),
+ (3, 'b'),
+ (5, '\\"'),
+ (6, 'c'),
+ (8, '\\\\'),
+ (9, '"'),
+ (10, 'd')])
+ self.assertEqual(list(Utils._iter_escaped_chars('a\\')),
+ [(0, 'a'), (1, '\\')])
+
+ def test_strip_quoted_realnames(self):
+ def check(addr, expected):
+ self.assertEqual(Utils._strip_quoted_realnames(addr), expected)
+
+ check('"Jane Doe" <jane@example.net>, "John Doe" <john@example.net>',
+ ' <jane@example.net>, <john@example.net>')
+ check(r'"Jane \"Doe\"." <jane@example.net>',
+ ' <jane@example.net>')
+
+ # special cases
+ check(r'before"name"after', 'beforeafter')
+ check(r'before"name"', 'before')
+ check(r'b"name"', 'b') # single char
+ check(r'"name"after', 'after')
+ check(r'"name"a', 'a') # single char
+ check(r'"name"', '')
+
+ # no change
+ for addr in (
+ 'Jane Doe <jane@example.net>, John Doe <john@example.net>',
+ 'lone " quote',
+ ):
+ self.assertEqual(Utils._strip_quoted_realnames(addr), addr)
+
+ def test_check_parenthesis(self):
+ addr = 'alice@example.net'
+ self.assertTrue(Utils._check_parenthesis('%s (Alice)' % addr))
+ self.assertFalse(Utils._check_parenthesis('%s )Alice(' % addr))
+ self.assertFalse(Utils._check_parenthesis('%s (Alice))' % addr))
+ self.assertFalse(Utils._check_parenthesis('%s ((Alice)' % addr))
+
+ # Ignore real name between quotes
+ self.assertTrue(Utils._check_parenthesis('")Alice((" %s' % addr))
+
+
def test_make_msgid_collisions(self):
# Test make_msgid uniqueness, even with multiple threads
class MsgidsThread(Thread):
--- a/Lib/email/test/test_email_renamed.py
+++ b/Lib/email/test/test_email_renamed.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# Copyright (C) 2001-2007 Python Software Foundation
# Contact: email-sig@python.org
# email package unit tests
@@ -2276,14 +2277,47 @@ Foo
('Bud Person', 'bperson@dom.ain')])
def test_getaddresses_nasty(self):
- eq = self.assertEqual
- eq(utils.getaddresses(['foo: ;']), [('', '')])
- eq(utils.getaddresses(
- ['[]*-- =~$']),
- [('', ''), ('', ''), ('', '*--')])
- eq(utils.getaddresses(
- ['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>']),
- [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')])
+ for addresses, expected in (
+ ([u'"Sürname, Firstname" <to@example.com>'],
+ [(u'Sürname, Firstname', 'to@example.com')]),
+
+ (['foo: ;'],
+ [('', '')]),
+
+ (['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>'],
+ [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]),
+
+ ([r'Pete(A nice \) chap) <pete(his account)@silly.test(his host)>'],
+ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]),
+
+ (['(Empty list)(start)Undisclosed recipients :(nobody(I know))'],
+ [('', '')]),
+
+ (['Mary <@machine.tld:mary@example.net>, , jdoe@test . example'],
+ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]),
+
+ (['John Doe <jdoe@machine(comment). example>'],
+ [('John Doe (comment)', 'jdoe@machine.example')]),
+
+ (['"Mary Smith: Personal Account" <smith@home.example>'],
+ [('Mary Smith: Personal Account', 'smith@home.example')]),
+
+ (['Undisclosed recipients:;'],
+ [('', '')]),
+
+ ([r'<boss@nil.test>, "Giant; \"Big\" Box" <bob@example.net>'],
+ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]),
+ ):
+ self.assertEqual(utils.getaddresses(addresses),
+ expected)
+ self.assertEqual(utils.getaddresses(addresses, strict=False),
+ expected)
+
+ addresses = ['[]*-- =~$']
+ self.assertEqual(utils.getaddresses(addresses),
+ [('', '')])
+ self.assertEqual(utils.getaddresses(addresses, strict=False),
+ [('', ''), ('', ''), ('', '*--')])
def test_getaddresses_embedded_comment(self):
"""Test proper handling of a nested comment"""
--- a/Lib/email/utils.py
+++ b/Lib/email/utils.py
@@ -100,15 +100,93 @@ def formataddr(pair):
return address
-
-def getaddresses(fieldvalues):
- """Return a list of (REALNAME, EMAIL) for each fieldvalue."""
- all = COMMASPACE.join(fieldvalues)
- a = _AddressList(all)
- return a.addresslist
+def _iter_escaped_chars(addr):
+ pos = 0
+ escape = False
+ for pos, ch in enumerate(addr):
+ if escape:
+ yield (pos, '\\' + ch)
+ escape = False
+ elif ch == '\\':
+ escape = True
+ else:
+ yield (pos, ch)
+ if escape:
+ yield (pos, '\\')
+
+
+def _strip_quoted_realnames(addr):
+ """Strip real names between quotes."""
+ if '"' not in addr:
+ # Fast path
+ return addr
+
+ start = 0
+ open_pos = None
+ result = []
+ for pos, ch in _iter_escaped_chars(addr):
+ if ch == '"':
+ if open_pos is None:
+ open_pos = pos
+ else:
+ if start != open_pos:
+ result.append(addr[start:open_pos])
+ start = pos + 1
+ open_pos = None
+
+ if start < len(addr):
+ result.append(addr[start:])
+
+ return ''.join(result)
+
+
+supports_strict_parsing = True
+
+def getaddresses(fieldvalues, strict=True):
+ """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue.
+
+ When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in
+ its place.
+
+ If strict is true, use a strict parser which rejects malformed inputs.
+ """
+
+ # If strict is true, if the resulting list of parsed addresses is greater
+ # than the number of fieldvalues in the input list, a parsing error has
+ # occurred and consequently a list containing a single empty 2-tuple [('',
+ # '')] is returned in its place. This is done to avoid invalid output.
+ #
+ # Malformed input: getaddresses(['alice@example.com <bob@example.com>'])
+ # Invalid output: [('', 'alice@example.com'), ('', 'bob@example.com')]
+ # Safe output: [('', '')]
+
+ if not strict:
+ all = COMMASPACE.join(unicode(v) for v in fieldvalues)
+ a = _AddressList(all)
+ return a.addresslist
+
+ fieldvalues = [unicode(v) for v in fieldvalues]
+ fieldvalues = _pre_parse_validation(fieldvalues)
+ addr = COMMASPACE.join(fieldvalues)
+ a = _AddressList(addr)
+ result = _post_parse_validation(a.addresslist)
+
+ # Treat output as invalid if the number of addresses is not equal to the
+ # expected number of addresses.
+ n = 0
+ for v in fieldvalues:
+ # When a comma is used in the Real Name part it is not a deliminator.
+ # So strip those out before counting the commas.
+ v = _strip_quoted_realnames(v)
+ # Expected number of addresses: 1 + number of commas
+ n += 1 + v.count(',')
+ if len(result) != n:
+ return [('', '')]
+
+ return result
+
-
ecre = re.compile(r'''
=\? # literal =?
(?P<charset>[^?]*?) # non-greedy up to the next ? is the charset
@@ -210,19 +288,74 @@ def parsedate_tz(data):
return _parsedate_tz(data)
-def parseaddr(addr):
+def parseaddr(addr, strict=True):
"""
Parse addr into its constituent realname and email address parts.
Return a tuple of realname and email address, unless the parse fails, in
which case return a 2-tuple of ('', '').
+
+ If strict is True, use a strict parser which rejects malformed inputs.
"""
- addrs = _AddressList(addr).addresslist
- if not addrs:
- return '', ''
+
+ if not strict:
+ addrs = _AddressList(addr).addresslist
+ if not addrs:
+ return ('', '')
+ return addrs[0]
+
+ if isinstance(addr, list):
+ addr = addr[0]
+
+ if not isinstance(addr, basestring):
+ return ('', '')
+
+ addr = _pre_parse_validation([addr])[0]
+ addrs = _post_parse_validation(_AddressList(addr).addresslist)
+
+ if not addrs or len(addrs) > 1:
+ return ('', '')
+
return addrs[0]
+def _check_parenthesis(addr):
+ # Ignore parenthesis in quoted real names.
+ addr = _strip_quoted_realnames(addr)
+
+ opens = 0
+ for pos, ch in _iter_escaped_chars(addr):
+ if ch == '(':
+ opens += 1
+ elif ch == ')':
+ opens -= 1
+ if opens < 0:
+ return False
+ return (opens == 0)
+
+
+def _pre_parse_validation(email_header_fields):
+ accepted_values = []
+ for v in email_header_fields:
+ if not _check_parenthesis(v):
+ v = "('', '')"
+ accepted_values.append(v)
+
+ return accepted_values
+
+
+def _post_parse_validation(parsed_email_header_tuples):
+ accepted_values = []
+ # The parser would have parsed a correctly formatted domain-literal
+ # The existence of an [ after parsing indicates a parsing failure
+ for v in parsed_email_header_tuples:
+ if '[' in v[1]:
+ v = ('', '')
+ accepted_values.append(v)
+
+ return accepted_values
+
+
# rfc822.unquote() doesn't properly de-backslash-ify in Python pre-2.3.
def unquote(str):
"""Remove quotes from a string."""
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst
@@ -0,0 +1,8 @@
+:func:`email.utils.getaddresses` and :func:`email.utils.parseaddr` now
+return ``('', '')`` 2-tuples in more situations where invalid email
+addresses are encountered instead of potentially inaccurate values. Add
+optional *strict* parameter to these two functions: use ``strict=False`` to
+get the old behavior, accept malformed inputs.
+``getattr(email.utils, 'supports_strict_parsing', False)`` can be use to check
+if the *strict* paramater is available. Patch by Thomas Dwyer and Victor
+Stinner to improve the CVE-2023-27043 fix.

View File

@ -0,0 +1,330 @@
From f0c1e55dfd28970196768a6997a6dc0eab0f5259 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?= <lukasz@langa.pl>
Date: Tue, 22 Aug 2023 17:39:17 +0200
Subject: [PATCH] gh-108310: Fix CVE-2023-40217: Check for & avoid the ssl
pre-close flaw
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Instances of `ssl.SSLSocket` were vulnerable to a bypass of the TLS handshake
and included protections (like certificate verification) and treating sent
unencrypted data as if it were post-handshake TLS encrypted data.
The vulnerability is caused when a socket is connected, data is sent by the
malicious peer and stored in a buffer, and then the malicious peer closes the
socket within a small timing window before the other peers TLS handshake can
begin. After this sequence of events the closed socket will not immediately
attempt a TLS handshake due to not being connected but will also allow the
buffered data to be read as if a successful TLS handshake had occurred.
Co-Authored-By: Gregory P. Smith [Google LLC] <greg@krypto.org>
---
Lib/ssl.py | 31 ++-
Lib/test/test_ssl.py | 215 ++++++++++++++++++
...-08-22-17-39-12.gh-issue-108310.fVM3sg.rst | 7 +
3 files changed, 252 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst
Index: Python-2.7.18/Lib/ssl.py
===================================================================
--- Python-2.7.18.orig/Lib/ssl.py
+++ Python-2.7.18/Lib/ssl.py
@@ -576,10 +576,13 @@ class SSLSocket(socket):
"in client mode")
if self._context.check_hostname and not server_hostname:
raise ValueError("check_hostname requires server_hostname")
+ self._closed = False
+ self._sslobj = None
self.server_side = server_side
self.server_hostname = server_hostname
self.do_handshake_on_connect = do_handshake_on_connect
self.suppress_ragged_eofs = suppress_ragged_eofs
+ sock_timeout = sock.gettimeout()
# See if we are connected
try:
@@ -588,11 +591,38 @@ class SSLSocket(socket):
if e.errno != errno.ENOTCONN:
raise
connected = False
+ blocking = self.gettimeout() == 0
+ self.setblocking(False)
+ try:
+ # We are not connected so this is not supposed to block, but
+ # testing revealed otherwise on macOS and Windows so we do
+ # the non-blocking dance regardless. Our raise when any data
+ # is found means consuming the data is harmless.
+ notconn_pre_handshake_data = self.recv(1)
+ except socket_error as e:
+ # EINVAL occurs for recv(1) on non-connected on unix sockets.
+ if e.errno not in (errno.ENOTCONN, errno.EINVAL):
+ raise
+ notconn_pre_handshake_data = b''
+ self.setblocking(blocking)
+ if notconn_pre_handshake_data:
+ # This prevents pending data sent to the socket before it was
+ # closed from escaping to the caller who could otherwise
+ # presume it came through a successful TLS connection.
+ reason = "Closed before TLS handshake with data in recv buffer."
+ notconn_pre_handshake_data_error = SSLError(e.errno, reason)
+ # Add the SSLError attributes that _ssl.c always adds.
+ notconn_pre_handshake_data_error.reason = reason
+ notconn_pre_handshake_data_error.library = None
+ try:
+ self.close()
+ except socket_error:
+ pass
+ raise notconn_pre_handshake_data_error
else:
connected = True
- self._closed = False
- self._sslobj = None
+ self.settimeout(sock_timeout) # Must come after setblocking() calls.
self._connected = connected
if connected:
# create the SSL object
Index: Python-2.7.18/Lib/test/test_ssl.py
===================================================================
--- Python-2.7.18.orig/Lib/test/test_ssl.py
+++ Python-2.7.18/Lib/test/test_ssl.py
@@ -20,6 +20,8 @@ import traceback
import weakref
import platform
import re
+import struct
+import httplib
import functools
from contextlib import closing
@@ -3262,6 +3264,217 @@ else:
self.assertRaises(ValueError, s.write, b'hello')
+def set_socket_so_linger_on_with_zero_timeout(sock):
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
+
+
+class TestPreHandshakeClose(unittest.TestCase):
+ """Verify behavior of close sockets with received data before to the handshake.
+ """
+
+ class SingleConnectionTestServerThread(threading.Thread):
+
+ def __init__(self, name=None, call_after_accept=None):
+ self.call_after_accept = call_after_accept
+ self.received_data = b'' # set by .run()
+ self.wrap_error = None # set by .run()
+ self.listener = None # set by .start()
+ self.port = None # set by .start()
+ super().__init__(name=name)
+
+ def __enter__(self):
+ self.start()
+ return self
+
+ def __exit__(self, *args):
+ try:
+ if self.listener:
+ self.listener.close()
+ except OSError:
+ pass
+ self.join()
+ self.wrap_error = None # avoid dangling references
+
+ def start(self):
+ self.ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
+ self.ssl_ctx.verify_mode = ssl.CERT_REQUIRED
+ self.ssl_ctx.load_verify_locations(cafile=ONLYCERT)
+ self.ssl_ctx.load_cert_chain(certfile=ONLYCERT, keyfile=ONLYKEY)
+ self.listener = socket.socket()
+ self.port = support.bind_port(self.listener)
+ self.listener.settimeout(2.0)
+ self.listener.listen(1)
+ super().start()
+
+ def run(self):
+ conn, address = self.listener.accept()
+ self.listener.close()
+ with conn:
+ if self.call_after_accept(conn):
+ return
+ try:
+ tls_socket = self.ssl_ctx.wrap_socket(conn, server_side=True)
+ except OSError as err: # ssl.SSLError inherits from OSError
+ self.wrap_error = err
+ else:
+ try:
+ self.received_data = tls_socket.recv(400)
+ except OSError:
+ pass # closed, protocol error, etc.
+
+ def non_linux_skip_if_other_okay_error(self, err):
+ if sys.platform == "linux":
+ return # Expect the full test setup to always work on Linux.
+ if (isinstance(err, ConnectionResetError) or
+ (isinstance(err, OSError) and err.errno == errno.EINVAL) or
+ re.search('wrong.version.number', getattr(err, "reason", ""), re.I)):
+ # On Windows the TCP RST leads to a ConnectionResetError
+ # (ECONNRESET) which Linux doesn't appear to surface to userspace.
+ # If wrap_socket() winds up on the "if connected:" path and doing
+ # the actual wrapping... we get an SSLError from OpenSSL. Typically
+ # WRONG_VERSION_NUMBER. While appropriate, neither is the scenario
+ # we're specifically trying to test. The way this test is written
+ # is known to work on Linux. We'll skip it anywhere else that it
+ # does not present as doing so.
+ self.skipTest("Could not recreate conditions on %s: %s" % (sys.platform, err))
+ # If maintaining this conditional winds up being a problem.
+ # just turn this into an unconditional skip anything but Linux.
+ # The important thing is that our CI has the logic covered.
+
+ def test_preauth_data_to_tls_server(self):
+ server_accept_called = threading.Event()
+ ready_for_server_wrap_socket = threading.Event()
+
+ def call_after_accept(unused):
+ server_accept_called.set()
+ if not ready_for_server_wrap_socket.wait(2.0):
+ raise RuntimeError("wrap_socket event never set, test may fail.")
+ return False # Tell the server thread to continue.
+
+ server = self.SingleConnectionTestServerThread(
+ call_after_accept=call_after_accept,
+ name="preauth_data_to_tls_server")
+ server.__enter__() # starts it
+ self.addCleanup(server.__exit__) # ... & unittest.TestCase stops it.
+
+ with socket.socket() as client:
+ client.connect(server.listener.getsockname())
+ # This forces an immediate connection close via RST on .close().
+ set_socket_so_linger_on_with_zero_timeout(client)
+ client.setblocking(False)
+
+ server_accept_called.wait()
+ client.send(b"DELETE /data HTTP/1.0\r\n\r\n")
+ client.close() # RST
+
+ ready_for_server_wrap_socket.set()
+ server.join()
+ wrap_error = server.wrap_error
+ self.assertEqual(b"", server.received_data)
+ self.assertIsInstance(wrap_error, OSError) # All platforms.
+ self.non_linux_skip_if_other_okay_error(wrap_error)
+ self.assertIsInstance(wrap_error, ssl.SSLError)
+ self.assertIn("before TLS handshake with data", wrap_error.args[1])
+ self.assertIn("before TLS handshake with data", wrap_error.reason)
+ self.assertNotEqual(0, wrap_error.args[0])
+ self.assertIsNone(wrap_error.library, msg="attr must exist")
+
+ def test_preauth_data_to_tls_client(self):
+ client_can_continue_with_wrap_socket = threading.Event()
+
+ def call_after_accept(conn_to_client):
+ # This forces an immediate connection close via RST on .close().
+ set_socket_so_linger_on_with_zero_timeout(conn_to_client)
+ conn_to_client.send(
+ b"HTTP/1.0 307 Temporary Redirect\r\n"
+ b"Location: https://example.com/someone-elses-server\r\n"
+ b"\r\n")
+ conn_to_client.close() # RST
+ client_can_continue_with_wrap_socket.set()
+ return True # Tell the server to stop.
+
+ server = self.SingleConnectionTestServerThread(
+ call_after_accept=call_after_accept,
+ name="preauth_data_to_tls_client")
+ server.__enter__() # starts it
+ self.addCleanup(server.__exit__) # ... & unittest.TestCase stops it.
+
+ # Redundant; call_after_accept sets SO_LINGER on the accepted conn.
+ set_socket_so_linger_on_with_zero_timeout(server.listener)
+
+ with socket.socket() as client:
+ client.connect(server.listener.getsockname())
+ if not client_can_continue_with_wrap_socket.wait(2.0):
+ self.fail("test server took too long.")
+ ssl_ctx = ssl.create_default_context()
+ try:
+ tls_client = ssl_ctx.wrap_socket(
+ client, server_hostname="localhost")
+ except OSError as err: # SSLError inherits from OSError
+ wrap_error = err
+ received_data = b""
+ else:
+ wrap_error = None
+ received_data = tls_client.recv(400)
+ tls_client.close()
+
+ server.join()
+ self.assertEqual(b"", received_data)
+ self.assertIsInstance(wrap_error, OSError) # All platforms.
+ self.non_linux_skip_if_other_okay_error(wrap_error)
+ self.assertIsInstance(wrap_error, ssl.SSLError)
+ self.assertIn("before TLS handshake with data", wrap_error.args[1])
+ self.assertIn("before TLS handshake with data", wrap_error.reason)
+ self.assertNotEqual(0, wrap_error.args[0])
+ self.assertIsNone(wrap_error.library, msg="attr must exist")
+
+ def test_https_client_non_tls_response_ignored(self):
+
+ server_responding = threading.Event()
+
+ class SynchronizedHTTPSConnection(httplib.HTTPSConnection):
+ def connect(self):
+ httplib.HTTPConnection.connect(self)
+ # Wait for our fault injection server to have done its thing.
+ if not server_responding.wait(1.0) and support.verbose:
+ sys.stdout.write("server_responding event never set.")
+ self.sock = self._context.wrap_socket(
+ self.sock, server_hostname=self.host)
+
+ def call_after_accept(conn_to_client):
+ # This forces an immediate connection close via RST on .close().
+ set_socket_so_linger_on_with_zero_timeout(conn_to_client)
+ conn_to_client.send(
+ b"HTTP/1.0 402 Payment Required\r\n"
+ b"\r\n")
+ conn_to_client.close() # RST
+ server_responding.set()
+ return True # Tell the server to stop.
+
+ server = self.SingleConnectionTestServerThread(
+ call_after_accept=call_after_accept,
+ name="non_tls_http_RST_responder")
+ server.__enter__() # starts it
+ self.addCleanup(server.__exit__) # ... & unittest.TestCase stops it.
+ # Redundant; call_after_accept sets SO_LINGER on the accepted conn.
+ set_socket_so_linger_on_with_zero_timeout(server.listener)
+
+ connection = SynchronizedHTTPSConnection(
+ "localhost",
+ port=server.port,
+ context=ssl.create_default_context(),
+ timeout=2.0,
+ )
+ # There are lots of reasons this raises as desired, long before this
+ # test was added. Sending the request requires a successful TLS wrapped
+ # socket; that fails if the connection is broken. It may seem pointless
+ # to test this. It serves as an illustration of something that we never
+ # want to happen... properly not happening.
+ with self.assertRaises(OSError) as err_ctx:
+ connection.request("HEAD", "/test", headers={"Host": "localhost"})
+ response = connection.getresponse()
+
+
def test_main(verbose=False):
if support.verbose:
plats = {
Index: Python-2.7.18/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst
===================================================================
--- /dev/null
+++ Python-2.7.18/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst
@@ -0,0 +1,7 @@
+Fixed an issue where instances of :class:`ssl.SSLSocket` were vulnerable to
+a bypass of the TLS handshake and included protections (like certificate
+verification) and treating sent unencrypted data as if it were
+post-handshake TLS encrypted data. Security issue reported as
+`CVE-2023-40217
+<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-40217>`_ by
+Aapo Oksman. Patch by Gregory P. Smith.

View File

@ -0,0 +1,32 @@
---
Lib/test/test_minidom.py | 3 ++-
Lib/test/test_xml_etree.py | 6 ------
2 files changed, 2 insertions(+), 7 deletions(-)
--- a/Lib/test/test_minidom.py
+++ b/Lib/test/test_minidom.py
@@ -1051,7 +1051,8 @@ class MinidomTest(unittest.TestCase):
# Verify that character decoding errors raise exceptions instead
# of crashing
- self.assertRaises(UnicodeDecodeError, parseString,
+ self.assertRaises((UnicodeDecodeError, xml.parsers.expat.ExpatError),
+ parseString,
'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
doc.unlink()
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -1482,12 +1482,6 @@ class BugsTest(unittest.TestCase):
b"<?xml version='1.0' encoding='ascii'?>\n"
b'<body>t&#227;g</body>')
- def test_issue3151(self):
- e = ET.XML('<prefix:localname xmlns:prefix="${stuff}"/>')
- self.assertEqual(e.tag, '{${stuff}}localname')
- t = ET.ElementTree(e)
- self.assertEqual(ET.tostring(e), b'<ns0:localname xmlns:ns0="${stuff}" />')
-
def test_issue6565(self):
elem = ET.XML("<body><tag/></body>")
self.assertEqual(summarize_list(elem), ['tag'])

View File

@ -0,0 +1,163 @@
From d8877aaabe9aa5d9b9904c222c552f3c6a85017c Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Wed, 17 Jan 2024 15:41:50 +0200
Subject: [PATCH] [CVE-2024-0450] Protect zipfile from "quoted-overlap" zipbomb
Raise BadZipFile when try to read an entry that overlaps with
other entry or central directory.
(cherry picked from commit 66363b9a7b9fe7c99eba3a185b74c5fdbf842eba)
From-PR: gh#python/cpython!110016
Fixes: gh#python/cpython#109858
Patch: CVE-2024-0450-zipfile-avoid-quoted-overlap-zipbomb.patch
---
Lib/test/test_zipfile.py | 66 +++++++++-
Lib/zipfile.py | 12 +
Misc/NEWS.d/next/Library/2023-09-28-13-15-51.gh-issue-109858.43e2dg.rst | 3
3 files changed, 78 insertions(+), 3 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-09-28-13-15-51.gh-issue-109858.43e2dg.rst
--- a/Lib/test/test_zipfile.py
+++ b/Lib/test/test_zipfile.py
@@ -1004,7 +1004,7 @@ class OtherTests(unittest.TestCase):
self.assertTrue(not chk)
def test_damaged_zipfile(self):
- """Check that zipfiles with missing bytes at the end raise BadZipFile."""
+ """Check that zipfiles with missing bytes at the end raise BadZipfile."""
# - Create a valid zip file
fp = io.BytesIO()
with zipfile.ZipFile(fp, mode="w") as zipf:
@@ -1012,7 +1012,7 @@ class OtherTests(unittest.TestCase):
zipfiledata = fp.getvalue()
# - Now create copies of it missing the last N bytes and make sure
- # a BadZipFile exception is raised when we try to open it
+ # a BadZipfile exception is raised when we try to open it
for N in range(len(zipfiledata)):
fp = io.BytesIO(zipfiledata[:N])
self.assertRaises(zipfile.BadZipfile, zipfile.ZipFile, fp)
@@ -1053,7 +1053,7 @@ class OtherTests(unittest.TestCase):
# quickly.
self.assertRaises(IOError, zipfile.ZipFile, TESTFN)
- def test_empty_file_raises_BadZipFile(self):
+ def test_empty_file_raises_BadZipfile(self):
with open(TESTFN, 'w') as f:
pass
self.assertRaises(zipfile.BadZipfile, zipfile.ZipFile, TESTFN)
@@ -1377,6 +1377,66 @@ class TestsWithRandomBinaryFiles(unittes
with open(TESTFN, "wb") as fp:
fp.write(self.data)
+ @skipUnless(zlib, "requires zlib")
+ def test_full_overlap(self):
+ data = (
+ b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e'
+ b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00a\xed'
+ b'\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\d\x0b`P'
+ b'K\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2'
+ b'\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00'
+ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00aPK'
+ b'\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e'
+ b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00\x00'
+ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00bPK\x05'
+ b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00'
+ b'\x00\x00\x00'
+ )
+ with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf:
+ self.assertEqual(zipf.namelist(), ['a', 'b'])
+ zi = zipf.getinfo('a')
+ self.assertEqual(zi.header_offset, 0)
+ self.assertEqual(zi.compress_size, 16)
+ self.assertEqual(zi.file_size, 1033)
+ zi = zipf.getinfo('b')
+ self.assertEqual(zi.header_offset, 0)
+ self.assertEqual(zi.compress_size, 16)
+ self.assertEqual(zi.file_size, 1033)
+ self.assertEqual(len(zipf.read('a')), 1033)
+ with self.assertRaisesRegexp(zipfile.BadZipfile, 'File name.*differ'):
+ zipf.read('b')
+
+ @skipUnless(zlib, "requires zlib")
+ def test_quoted_overlap(self):
+ data = (
+ b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05Y\xfc'
+ b'8\x044\x00\x00\x00(\x04\x00\x00\x01\x00\x00\x00a\x00'
+ b'\x1f\x00\xe0\xffPK\x03\x04\x14\x00\x00\x00\x08\x00\xa0l'
+ b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00'
+ b'\x00\x00b\xed\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\'
+ b'd\x0b`PK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0'
+ b'lH\x05Y\xfc8\x044\x00\x00\x00(\x04\x00\x00\x01'
+ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ b'\x00aPK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0l'
+ b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00'
+ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x00\x00'
+ b'bPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00'
+ b'\x00S\x00\x00\x00\x00\x00'
+ )
+ with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf:
+ self.assertEqual(zipf.namelist(), ['a', 'b'])
+ zi = zipf.getinfo('a')
+ self.assertEqual(zi.header_offset, 0)
+ self.assertEqual(zi.compress_size, 52)
+ self.assertEqual(zi.file_size, 1064)
+ zi = zipf.getinfo('b')
+ self.assertEqual(zi.header_offset, 36)
+ self.assertEqual(zi.compress_size, 16)
+ self.assertEqual(zi.file_size, 1033)
+ with self.assertRaisesRegexp(zipfile.BadZipfile, 'Overlapped entries'):
+ zipf.read('a')
+ self.assertEqual(len(zipf.read('b')), 1033)
+
def tearDown(self):
unlink(TESTFN)
unlink(TESTFN2)
--- a/Lib/zipfile.py
+++ b/Lib/zipfile.py
@@ -305,6 +305,7 @@ class ZipInfo (object):
'compress_size',
'file_size',
'_raw_time',
+ '_end_offset',
)
def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
@@ -343,6 +344,7 @@ class ZipInfo (object):
self.volume = 0 # Volume number of file header
self.internal_attr = 0 # Internal attributes
self.external_attr = 0 # External file attributes
+ self._end_offset = None # Start of the next local header or central directory
# Other attributes are set by class ZipFile:
# header_offset Byte offset to the file header
# CRC CRC-32 of the uncompressed file
@@ -891,6 +893,12 @@ class ZipFile(object):
if self.debug > 2:
print "total", total
+ end_offset = self.start_dir
+ for zinfo in sorted(self.filelist,
+ key=lambda zinfo: zinfo.header_offset,
+ reverse=True):
+ zinfo._end_offset = end_offset
+ end_offset = zinfo.header_offset
def namelist(self):
"""Return a list of file names in the archive."""
@@ -1002,6 +1010,10 @@ class ZipFile(object):
'File name in directory "%s" and header "%s" differ.' % (
zinfo.orig_filename, fname)
+ if (zinfo._end_offset is not None and
+ zef_file.tell() + zinfo.compress_size > zinfo._end_offset):
+ raise BadZipfile("Overlapped entries: {!r} (possible zip bomb)".format(zinfo.orig_filename))
+
# check for encrypted flag & handle password
is_encrypted = zinfo.flag_bits & 0x1
zd = None
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-09-28-13-15-51.gh-issue-109858.43e2dg.rst
@@ -0,0 +1,3 @@
+Protect :mod:`zipfile` from "quoted-overlap" zipbomb. It now raises
+BadZipfile when try to read an entry that overlaps with other entry or
+central directory.

View File

@ -0,0 +1,28 @@
---
Doc/tools/extensions/pyspecific.py | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
--- a/Doc/tools/extensions/pyspecific.py
+++ b/Doc/tools/extensions/pyspecific.py
@@ -31,14 +31,17 @@ Body.enum.converters['loweralpha'] = \
# doctest docs themselves
orig_visit_literal_block = HTMLTranslator.visit_literal_block
def new_visit_literal_block(self, node):
+ old_trim_doctest_flags = None
meta = self.builder.env.metadata[self.builder.current_docname]
- old_trim_doctest_flags = self.highlighter.trim_doctest_flags
- if 'keepdoctest' in meta:
- self.highlighter.trim_doctest_flags = False
+ if hasattr(self.highlighter, 'trim_doctest_flags'):
+ old_trim_doctest_flags = self.highlighter.trim_doctest_flags
+ if 'keepdoctest' in meta:
+ self.highlighter.trim_doctest_flags = False
try:
orig_visit_literal_block(self, node)
finally:
- self.highlighter.trim_doctest_flags = old_trim_doctest_flags
+ if old_trim_doctest_flags is not None:
+ self.highlighter.trim_doctest_flags = old_trim_doctest_flags
HTMLTranslator.visit_literal_block = new_visit_literal_block

3
Python-2.7.18.tar.xz Normal file
View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b62c0e7937551d0cc02b8fd5cb0f544f9405bafc9a54d3808ed4594812edef43
size 12854736

16
Python-2.7.18.tar.xz.asc Normal file
View File

@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEwB4crV6ixPC441cVBMNnwhit1P8FAl6cx00ACgkQBMNnwhit
1P8kiBAAmGj5Nz8rsDoqRppDfWuk/oLU1WwXIixFOuzrrIcSnUDGtZgikIlA0q1z
At/09+mbQMSv93/oa+ISCQujvH5QTbKqOoYYdBGdsK6XZevrGM1UO3eRaldBElQ0
03zIT7d9OyvbvvvegsqaCMFoGhcAmnp6AomXFt20U80tIaCBCftGKIfQQXR/aTfz
w3F7s+ZrzLd2mj9rVtld6KPs9ZuTl3xK1YlsfKvXnLwK0v9h7shVvkj9vKnolwPI
Ykl1FDI0p/gHbkRzC0D10zOv58mO4jrkezlq1ZKVwu7hgGFVXt1ZudwbpIWz8cl0
AHcEK+ls9F9fw6bvRJPHi0L/jvvr58+3hg1iwJW24eYvP2GuRSRk1GF3FroARll7
+PW6y+kyrjhyznv0KVY5efEgJQRGJ4o6d5PvWKIWiwL6HycAXfUt7248S0N3acKZ
Am4UVCRXwhCB0+xENAaT/KtMK/kvl5G9bVLSpah0LlSZ0u/X86zhyitVky3LD/el
JRrHskXIA4wDcxfv503tEvRm9vLOdr0XwAyZ9qh7NGfmmAT2W/bKa3qlM6DJ027c
mRl0VKmiseh4r3JIOAqkDFUNbvjKhteA4HeTrOxsqacnzWTH+tvB2Pm3Qpl/oRhM
iAsGICpa9IMFmhmhoWjdpacXIiPaGhJA9AC3lufOPgIqMVvwsQ4=
=V2yl
-----END PGP SIGNATURE-----

21
README.SUSE Normal file
View File

@ -0,0 +1,21 @@
Python in SUSE
==============
* Documentation *
You can find documentation in seprarate packages: python-doc and
python-doc-pdf. These contan following documents:
Tutorial, What's New in Python, Global Module Index, Library Reference,
Macintosh Module Reference, Installing Python Modules, Distributing Python
Modules, Language Reference, Extending and Embedding, Python/C API,
Documenting Python
The python-doc package constains many text files from source tarball.
* Interactive mode *
Interactive mode is by default enhanced with of history and command completion.
If you don't like these features, you can unset PYTHONSTARTUP variable in your
.profile or disable it system wide in /etc/profile.d/python.sh.

4
_multibuild Normal file
View File

@ -0,0 +1,4 @@
<multibuild>
<package>python-base</package>
<package>python-doc</package>
</multibuild>

View File

@ -0,0 +1,40 @@
Index: Python-2.7.17/Lib/distutils/command/install.py
===================================================================
--- Python-2.7.17.orig/Lib/distutils/command/install.py
+++ Python-2.7.17/Lib/distutils/command/install.py
@@ -431,8 +431,18 @@ class install (Command):
raise DistutilsOptionError, \
"must not supply exec-prefix without prefix"
- self.prefix = os.path.normpath(sys.prefix)
- self.exec_prefix = os.path.normpath(sys.exec_prefix)
+ # self.prefix is set to sys.prefix + /local/
+ # if neither RPM build nor virtual environment is
+ # detected to make pip and distutils install packages
+ # into the separate location.
+ if (not hasattr(sys, 'real_prefix') and
+ 'RPM_BUILD_ROOT' not in os.environ):
+ addition = "/local"
+ else:
+ addition = ""
+
+ self.prefix = os.path.normpath(sys.prefix) + addition
+ self.exec_prefix = os.path.normpath(sys.exec_prefix) + addition
else:
if self.exec_prefix is None:
Index: Python-2.7.17/Lib/site.py
===================================================================
--- Python-2.7.17.orig/Lib/site.py
+++ Python-2.7.17/Lib/site.py
@@ -291,6 +291,10 @@ def getsitepackages():
sitepackages = []
seen = set()
+ # '/usr/local' is included in PREFIXES if RPM build is not detected
+ # to make packages installed into this location visible.
+ if ENABLE_USER_SITE and 'RPM_BUILD_ROOT' not in os.environ:
+ PREFIXES.insert(0, "/usr/local")
for prefix in PREFIXES:
if not prefix or prefix in seen:
continue

4
baselibs.conf Normal file
View File

@ -0,0 +1,4 @@
python
requires "python-base-<targettype> = <version>"
python-base
libpython2_7-1_0

View File

@ -0,0 +1,142 @@
From 9d3b6b2472f7c7ef841e652825de652bc8af85d7 Mon Sep 17 00:00:00 2001
From: "Miss Islington (bot)"
<31488909+miss-islington@users.noreply.github.com>
Date: Tue, 24 Aug 2021 08:07:31 -0700
Subject: [PATCH] [3.9] bpo-34990: Treat the pyc header's mtime in compileall
as an unsigned int (GH-19708)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
(cherry picked from commit bb21e28fd08f894ceff2405544a2f257d42b1354)
Co-authored-by: Ammar Askar <ammar@ammaraskar.com>
Co-authored-by: Stéphane Wirtel <stephane@wirtel.be>
ported to python-2.7 by Bernhard M. Wiedemann <bwiedemann suse de>
diff --git a/Lib/compileall.py b/Lib/compileall.py
index 5cfa8be..193147e 100644
--- a/Lib/compileall.py
+++ b/Lib/compileall.py
@@ -85,7 +85,7 @@ def compile_file(fullname, ddir=None, force=0, rx=None, quiet=0):
if not force:
try:
mtime = int(os.stat(fullname).st_mtime)
- expect = struct.pack('<4sl', imp.get_magic(), mtime)
+ expect = struct.pack('<4sL', imp.get_magic(), mtime & 0xFFFFFFFF)
cfile = fullname + (__debug__ and 'c' or 'o')
with open(cfile, 'rb') as chandle:
actual = chandle.read(8)
diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py
index d3a26db..0907f59 100644
--- a/Lib/test/test_compileall.py
+++ b/Lib/test/test_compileall.py
@@ -28,7 +28,7 @@ class CompileallTests(unittest.TestCase):
with open(self.bc_path, 'rb') as file:
data = file.read(8)
mtime = int(os.stat(self.source_path).st_mtime)
- compare = struct.pack('<4sl', imp.get_magic(), mtime)
+ compare = struct.pack('<4sL', imp.get_magic(), mtime & 0xFFFFFFFF)
return data, compare
@unittest.skipUnless(hasattr(os, 'stat'), 'test needs os.stat()')
@@ -48,7 +48,7 @@ class CompileallTests(unittest.TestCase):
def test_mtime(self):
# Test a change in mtime leads to a new .pyc.
- self.recreation_check(struct.pack('<4sl', imp.get_magic(), 1))
+ self.recreation_check(struct.pack('<4sL', imp.get_magic(), 1))
def test_magic_number(self):
# Test a change in mtime leads to a new .pyc.
diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py
index a66738a..e333582 100644
--- a/Lib/test/test_zipimport.py
+++ b/Lib/test/test_zipimport.py
@@ -27,13 +27,7 @@ raise_src = 'def do_raise(): raise TypeError\n'
def make_pyc(co, mtime):
data = marshal.dumps(co)
- if type(mtime) is type(0.0):
- # Mac mtimes need a bit of special casing
- if mtime < 0x7fffffff:
- mtime = int(mtime)
- else:
- mtime = int(-0x100000000L + long(mtime))
- pyc = imp.get_magic() + struct.pack("<i", int(mtime)) + data
+ pyc = imp.get_magic() + struct.pack("<L", int(mtime) & 0xFFFFFFFF) + data
return pyc
def module_path_to_dotted_name(path):
@@ -184,6 +178,14 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase):
TESTMOD + pyc_ext: (NOW, badtime_pyc)}
self.doTest(".py", files, TESTMOD)
+ def test2038MTime(self):
+ # Make sure we can handle mtimes larger than what a 32-bit signed number
+ # can hold.
+ twenty_thirty_eight_pyc = make_pyc(test_co, 2**32 - 1)
+ files = {TESTMOD + ".py": (NOW, test_src),
+ TESTMOD + pyc_ext: (NOW, twenty_thirty_eight_pyc)}
+ self.doTest(".py", files, TESTMOD)
+
def testPackage(self):
packdir = TESTPACK + os.sep
files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc),
==========
Author: Bernhard M. Wiedemann <bwiedemann suse de>
Date: 2022-09-13
More y2038 fixes that are only needed for python2.7
diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py
index 6515945..21d52bb 100644
--- a/Lib/compiler/pycodegen.py
+++ b/Lib/compiler/pycodegen.py
@@ -128,7 +128,7 @@ class Module(AbstractCompileMode):
# to indicate the type of the value. simplest way to get the
# same effect is to call marshal and then skip the code.
mtime = os.path.getmtime(self.filename)
- mtime = struct.pack('<i', mtime)
+ mtime = struct.pack('<L', mtime & 0xFFFFFFFF)
return self.MAGIC + mtime
class LocalNameFinder:
diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py
index cdb1af5..6344ef2 100644
--- a/Lib/test/test_gzip.py
+++ b/Lib/test/test_gzip.py
@@ -265,7 +265,7 @@ class TestGzip(unittest.TestCase):
self.assertEqual(flagsByte, '\x08') # only the FNAME flag is set
mtimeBytes = fRead.read(4)
- self.assertEqual(mtimeBytes, struct.pack('<i', mtime)) # little-endian
+ self.assertEqual(mtimeBytes, struct.pack('<L', mtime & 0xFFFFFFFF)) # little-endian
xflByte = fRead.read(1)
self.assertEqual(xflByte, '\x02') # maximum compression
diff --git a/Python/import.c b/Python/import.c
index b79354b..3efd17f 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -810,7 +810,7 @@ check_compiled_module(char *pathname, time_t mtime, char *cpathname)
{
FILE *fp;
long magic;
- long pyc_mtime;
+ unsigned long pyc_mtime;
fp = fopen(cpathname, "rb");
if (fp == NULL)
@@ -823,7 +823,7 @@ check_compiled_module(char *pathname, time_t mtime, char *cpathname)
return NULL;
}
pyc_mtime = PyMarshal_ReadLongFromFile(fp);
- if (pyc_mtime != mtime) {
+ if ((pyc_mtime&0xFFFFFFFF) != (((unsigned long)mtime)&0xFFFFFFFF)) {
if (Py_VerboseFlag)
PySys_WriteStderr("# %s has bad mtime\n", cpathname);
fclose(fp);

View File

@ -0,0 +1,29 @@
From 603a4461e3c889b06a5d78e57594ebbc580f1c03 Mon Sep 17 00:00:00 2001
From: Ivan Pozdeev <vano@mail.mipt.ru>
Date: Fri, 1 Mar 2019 21:44:24 +0300
Subject: [PATCH] Fix AttributeError on sysconfig._CONFIG_VARS.clear() if
test_site is run separately
---
Lib/test/test_site.py | 4 +++-
.../next/Tests/2019-03-01-21-45-13.bpo-36160.4JjrqB.rst | 2 ++
2 files changed, 5 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Tests/2019-03-01-21-45-13.bpo-36160.4JjrqB.rst
--- a/Lib/test/test_site.py
+++ b/Lib/test/test_site.py
@@ -47,6 +47,9 @@ def setUpModule():
else:
raise
+ # sysconfig._CONFIG_VARS is None until the first call to this function
+ sysconfig.get_config_vars()
+
def tearDownModule():
sys.path[:] = OLD_SYS_PATH
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2019-03-01-21-45-13.bpo-36160.4JjrqB.rst
@@ -0,0 +1,2 @@
+Fix AttributeError on sysconfig._CONFIG_VARS.clear() if test_site is run
+separately

View File

@ -0,0 +1,13 @@
diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py
index 86a85c1..66bf0c2 100644
--- a/Lib/distutils/command/build_ext.py
+++ b/Lib/distutils/command/build_ext.py
@@ -455,7 +455,7 @@ class build_ext (Command):
("in 'ext_modules' option (extension '%s'), " +
"'sources' must be present and must be " +
"a list of source filenames") % ext.name
- sources = list(sources)
+ sources = sorted(sources)
ext_path = self.get_ext_fullpath(ext.name)
depends = sources + ext.depends

View File

@ -0,0 +1,11 @@
--- a/configure.ac
+++ b/configure.ac
@@ -19,7 +19,7 @@ AC_SUBST(host)
# pybuilddir.txt will be created by --generate-posix-vars in the Makefile
rm -f pybuilddir.txt
-AC_CHECK_PROGS(PYTHON_FOR_REGEN, python$PACKAGE_VERSION python3 python, python3)
+AC_CHECK_PROGS(PYTHON_FOR_REGEN, python$PACKAGE_VERSION python python2, python)
AC_SUBST(PYTHON_FOR_REGEN)
if test "$cross_compiling" = yes; then

View File

@ -0,0 +1,15 @@
---
Lib/test/test_ssl.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -1052,7 +1052,7 @@ class ContextTests(unittest.TestCase):
def test_load_dh_params(self):
- filename = u'dhpäräm.pem'
+ filename = u'dhparam.pem'
fs_encoding = sys.getfilesystemencoding()
try:
filename.encode(fs_encoding)

35
idle.appdata.xml Normal file
View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2017 Zbigniew Jędrzejewski-Szmek -->
<application>
<id type="desktop">idle.desktop</id>
<name>IDLE</name>
<metadata_licence>CC0</metadata_licence>
<project_license>Python-2.0</project_license>
<summary>Python Integrated Development and Learning Environment</summary>
<description>
<p>
IDLE is Pythons Integrated Development and Learning Environment.
The GUI is uniform between Windows, Unix, and Mac OS X.
IDLE provides an easy way to start writing, running, and debugging
Python code.
</p>
<p>
IDLE is written in pure Python, and uses the tkinter GUI toolkit.
It provides:
</p>
<ul>
<li>a Python shell window (interactive interpreter) with colorizing of code input, output, and error messages,</li>
<li>a multi-window text editor with multiple undo, Python colorizing, smart indent, call tips, auto completion, and other features,</li>
<li>search within any window, replace within editor windows, and search through multiple files (grep),</li>
<li>a debugger with persistent breakpoints, stepping, and viewing of global and local namespaces.</li>
</ul>
</description>
<url type="homepage">https://docs.python.org/2.7/library/idle.html</url>
<screenshots>
<screenshot type="default">http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-main-window.png</screenshot>
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-class-browser.png</screenshot>
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-code-viewer.png</screenshot>
</screenshots>
<update_contact>zbyszek@in.waw.pl</update_contact>
</application>

12
idle.desktop Normal file
View File

@ -0,0 +1,12 @@
[Desktop Entry]
Version=1.0
Name=IDLE
GenericName=Python IDE
Comment=Python 2.7 Integrated Development and Learning Environment
Exec=idle %F
TryExec=idle
Terminal=false
Type=Application
Icon=idle
Categories=Development;IDE;
MimeType=text/x-python;

1
local.pth Normal file
View File

@ -0,0 +1 @@
import site; import sys; site.addsitedir("/usr/local/" + sys.lib + "/python"+sys.version[:3]+"/site-packages", set()); sys.lib != "lib" and site.addsitedir("/usr/local/lib/python"+sys.version[:3]+"/site-packages", set())

21
macros.python2 Normal file
View File

@ -0,0 +1,21 @@
# legacy macros. commented but kept for the sake of possible recovery of their values
%py_prefix %(python -c "import sys; print sys.prefix" 2>/dev/null || echo PYTHON-NOT-FOUND)
%py_libdir %{py_prefix}/%{_lib}/python%{py_ver}
%py_incdir %{py_prefix}/include/python%{py_ver}
%py_sitedir %{py_libdir}/site-packages
# these might be still in use somewhere
%py_compile(O) \
find %1 -name '*.pyc' -exec rm -f {} \\; \
python2 -c "import sys, os, compileall; br='%{buildroot}'; compileall.compile_dir(sys.argv[1], ddir=br and (sys.argv[1][len(os.path.abspath(br)):]+'/') or None)" %1 \
%{-O: \
find %1 -name '*.pyo' -exec rm -f {} \\; \
python2 -O -c "import sys, os, compileall; br='%{buildroot}'; compileall.compile_dir(sys.argv[1], ddir=br and (sys.argv[1][len(os.path.abspath(br)):]+'/') or None)" %1 \
}
%py_requires(d) \
BuildRequires: /usr/bin/python %{-d:python-devel} \
PreReq: python = %{py_ver}
# new-style macros relying on python-rpm-macros package
%have_python2 1
%python2_package_prefix python2

View File

@ -0,0 +1,30 @@
From 4fa35e8b1ebb2a8e88ba7c4c9cd2a17b35638ee6 Mon Sep 17 00:00:00 2001
From: Dimitri John Ledkov <xnox@ubuntu.com>
Date: Fri, 28 Sep 2018 16:34:16 +0100
Subject: [PATCH] bpo-34834: Fix test_ssl.test_options to account for
OP_ENABLE_MIDDLEBOX_COMPAT.
Signed-off-by: Dimitri John Ledkov <xnox@ubuntu.com>
https://bugs.python.org/issue34834
---
Lib/test/test_ssl.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -838,8 +838,12 @@ class ContextTests(unittest.TestCase):
default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3)
# SSLContext also enables these by default
default |= (OP_NO_COMPRESSION | OP_CIPHER_SERVER_PREFERENCE |
- OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE |
- OP_ENABLE_MIDDLEBOX_COMPAT)
+ ssl.OP_SINGLE_DH_USE | ssl.OP_SINGLE_ECDH_USE)
+ if not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 1):
+ # define MIDDLEBOX constant, as python2.7 does not know about it
+ # but it is used by default.
+ OP_ENABLE_MIDDLEBOX_COMPAT = 1048576L
+ default |= OP_ENABLE_MIDDLEBOX_COMPAT
self.assertEqual(default, ctx.options)
ctx.options |= ssl.OP_NO_TLSv1
self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options)

View File

@ -0,0 +1,21 @@
---
Lib/test/test_ssl.py | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -838,12 +838,8 @@ class ContextTests(unittest.TestCase):
default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3)
# SSLContext also enables these by default
default |= (OP_NO_COMPRESSION | OP_CIPHER_SERVER_PREFERENCE |
- ssl.OP_SINGLE_DH_USE | ssl.OP_SINGLE_ECDH_USE)
- if not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 1):
- # define MIDDLEBOX constant, as python2.7 does not know about it
- # but it is used by default.
- OP_ENABLE_MIDDLEBOX_COMPAT = 1048576L
- default |= OP_ENABLE_MIDDLEBOX_COMPAT
+ OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE |
+ OP_ENABLE_MIDDLEBOX_COMPAT)
self.assertEqual(default, ctx.options)
ctx.options |= ssl.OP_NO_TLSv1
self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options)

BIN
pip-20.0.2-py2.py3-none-any.whl (Stored with Git LFS) Normal file

Binary file not shown.

15
pre_checkin.sh Normal file
View File

@ -0,0 +1,15 @@
#!/bin/bash
# This script is called automatically during autobuild checkin.
for spec in python-doc.spec python.spec; do
{ sed -n -e '1,/COMMON-PATCH-BEGIN/p' $spec
sed -n -e '/COMMON-PATCH-BEGIN/,/COMMON-PATCH-END/p' python-base.spec
sed -n -e '/COMMON-PATCH-END/,/COMMON-PREP-BEGIN/p' $spec
sed -n -e '/COMMON-PREP-BEGIN/,/COMMON-PREP-END/p' python-base.spec
sed -n -e '/COMMON-PREP-END/,$p' $spec;
} | uniq > $spec.tmp && mv $spec.tmp $spec
done
# I really don't want to keep all three *.changes files separate
cp python-base.changes python.changes
cp python-base.changes python-doc.changes

12
python-2.5.1-sqlite.patch Normal file
View File

@ -0,0 +1,12 @@
--- a/Modules/_sqlite/cursor.c
+++ b/Modules/_sqlite/cursor.c
@@ -829,6 +829,9 @@ PyObject* pysqlite_cursor_executescript(
goto error;
}
+ if (! statement)
+ break;
+
/* execute statement, and ignore results of SELECT statements */
rc = SQLITE_ROW;
while (rc == SQLITE_ROW) {

View File

@ -0,0 +1,14 @@
--- a/Lib/gettext.py
+++ b/Lib/gettext.py
@@ -387,8 +387,9 @@ class GNUTranslations(NullTranslations):
self._charset = v.split('charset=')[1]
elif k == 'plural-forms':
v = v.split(';')
- plural = v[1].split('plural=')[1]
- self.plural = c2py(plural)
+ if len(v) > 1:
+ plural = v[1].split('plural=')[1]
+ self.plural = c2py(plural)
# Note: we unconditionally convert both msgids and msgstrs to
# Unicode using the character encoding specified in the charset
# parameter of the Content-Type header. The gettext documentation

View File

@ -0,0 +1,11 @@
--- a/Modules/_curses_panel.c
+++ b/Modules/_curses_panel.c
@@ -14,7 +14,7 @@
#include "py_curses.h"
-#include <panel.h>
+#include <ncurses/panel.h>
static PyObject *PyCursesError;

11
python-2.7-dirs.patch Normal file
View File

@ -0,0 +1,11 @@
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -110,7 +110,7 @@ LIBDIR= @libdir@
MANDIR= @mandir@
INCLUDEDIR= @includedir@
CONFINCLUDEDIR= $(exec_prefix)/include
-SCRIPTDIR= $(prefix)/lib
+SCRIPTDIR= @libdir@
# Detailed destination directories
BINLIBDEST= $(LIBDIR)/python$(VERSION)

View File

@ -0,0 +1,25 @@
---
Lib/test/test_ssl.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -152,9 +152,7 @@ def skip_if_broken_ubuntu_ssl(func):
try:
ssl.SSLContext(ssl.PROTOCOL_SSLv2)
except ssl.SSLError:
- if (ssl.OPENSSL_VERSION_INFO == (0, 9, 8, 15, 15) and
- platform.linux_distribution() == ('debian', 'squeeze/sid', '')):
- raise unittest.SkipTest("Patched Ubuntu OpenSSL breaks behaviour")
+ raise unittest.SkipTest("Test fails on SLE-12")
return func(*args, **kwargs)
return f
else:
@@ -1280,6 +1278,7 @@ class ContextTests(unittest.TestCase):
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
self._assert_context_options(ctx)
+ @unittest.skip("Test fails on SLE-12")
def test__https_verify_certificates(self):
# Unit test to check the contect factory mapping
# The factories themselves are tested above

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:55cfc0527ec38284ae56d90b165f099184d6c4e2f1ba604af9e462a66552fcaa
size 11455638

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:76e56fe618a6d5d1cd7b90e73d46fa1a4d0b3e5bbdfdce6c5d59cff9d49ed749
size 11455851

View File

@ -0,0 +1,18 @@
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -647,8 +647,15 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \
-DGITVERSION="\"`LC_ALL=C $(GITVERSION)`\"" \
-DGITTAG="\"`LC_ALL=C $(GITTAG)`\"" \
-DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \
+ -DDATE="\"`LC_ALL=C date -u -r Makefile.pre.in +"%b %d %Y"`\"" \
+ -DTIME="\"`LC_ALL=C date -u -r Makefile.pre.in +"%T"`\"" \
-o $@ $(srcdir)/Modules/getbuildinfo.c
+Python/getcompiler.o: $(srcdir)/Python/getcompiler.c Makefile
+ $(CC) -c $(PY_CFLAGS) \
+ -DCOMPILER='"[GCC]"' \
+ -o $@ $(srcdir)/Python/getcompiler.c
+
Modules/getpath.o: $(srcdir)/Modules/getpath.c Makefile
$(CC) -c $(PY_CFLAGS) -DPYTHONPATH='"$(PYTHONPATH)"' \
-DPREFIX='"$(prefix)"' \

View File

@ -0,0 +1,85 @@
---
Python/sysmodule.c | 26 +++++++++++++++++++-------
configure.ac | 2 +-
pyconfig.h.in | 3 +++
3 files changed, 23 insertions(+), 8 deletions(-)
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1638,7 +1638,20 @@ PySys_SetArgvEx(int argc, char **argv, i
char *p = NULL;
Py_ssize_t n = 0;
PyObject *a;
-#ifdef HAVE_READLINK
+#ifdef HAVE_CANONICALIZE_FILE_NAME
+ int errnum;
+
+ if (argc > 0 && argv0 != NULL && strcmp(argv0, "-c") != 0) {
+ argv0 = canonicalize_file_name(argv0);
+ if (argv0 == NULL) argv0 = strdup(argv[0]);
+ }
+#elif defined(HAVE_REALPATH)
+ if (argc > 0 && argv0 != NULL && strcmp(argv0, "-c") != 0) {
+ if (realpath(argv0, fullpath)) {
+ argv0 = fullpath;
+ }
+ }
+#elif defined(HAVE_READLINK)
char link[MAXPATHLEN+1];
char argv0copy[2*MAXPATHLEN+1];
int nr = 0;
@@ -1665,7 +1678,8 @@ PySys_SetArgvEx(int argc, char **argv, i
}
}
}
-#endif /* HAVE_READLINK */
+#endif /* resolve method selection */
+
#if SEP == '\\' /* Special case for MS filename syntax */
if (argc > 0 && argv0 != NULL && strcmp(argv0, "-c") != 0) {
char *q;
@@ -1694,11 +1708,6 @@ PySys_SetArgvEx(int argc, char **argv, i
}
#else /* All other filename syntaxes */
if (argc > 0 && argv0 != NULL && strcmp(argv0, "-c") != 0) {
-#if defined(HAVE_REALPATH)
- if (realpath(argv0, fullpath)) {
- argv0 = fullpath;
- }
-#endif
p = strrchr(argv0, SEP);
}
if (p != NULL) {
@@ -1716,6 +1725,9 @@ PySys_SetArgvEx(int argc, char **argv, i
a = PyString_FromStringAndSize(argv0, n);
if (a == NULL)
Py_FatalError("no mem for sys.path insertion");
+#ifdef HAVE_CANONICALIZE_FILE_NAME
+ if (argc > 0 && argv0 != NULL && strcmp(argv0, "-c") != 0) free(argv0);
+#endif /* HAVE_CANONICALIZE_FILE_NAME */
if (PyList_Insert(path, 0, a) < 0)
Py_FatalError("sys.path.insert(0) failed");
Py_DECREF(a);
--- a/configure.ac
+++ b/configure.ac
@@ -3165,7 +3165,7 @@ AC_CHECK_FUNCS(alarm setitimer getitimer
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
initgroups kill killpg lchown lstat mkfifo mknod mktime mmap \
mremap nice pathconf pause plock poll pthread_init \
- putenv readlink realpath \
+ putenv readlink realpath canonicalize_file_name \
select sem_open sem_timedwait sem_getvalue sem_unlink setegid seteuid \
setgid \
setlocale setregid setreuid setsid setpgid setpgrp setuid setvbuf snprintf \
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -109,6 +109,9 @@
/* Define to 1 if you have the 'chflags' function. */
#undef HAVE_CHFLAGS
+/* Define to 1 if you have the `canonicalize_file_name' function. */
+#undef HAVE_CANONICALIZE_FILE_NAME
+
/* Define to 1 if you have the `chown' function. */
#undef HAVE_CHOWN

436
python-2.7.5-multilib.patch Normal file
View File

@ -0,0 +1,436 @@
---
Include/pythonrun.h | 2 +
Lib/distutils/command/install.py | 4 ++-
Lib/distutils/sysconfig.py | 7 ++++--
Lib/pydoc.py | 2 -
Lib/site.py | 40 +++++++++++++++++++++++++++------------
Lib/sysconfig.py | 12 +++++------
Lib/test/test_dl.py | 5 ++--
Lib/test/test_site.py | 16 +++++++++++----
Lib/trace.py | 4 +--
Makefile.pre.in | 5 +++-
Modules/getpath.c | 4 ++-
Python/getplatform.c | 20 +++++++++++++++++++
Python/sysmodule.c | 4 +++
configure.ac | 35 ++++++++++++++++++++++++++++++++++
setup.py | 15 ++++++--------
15 files changed, 135 insertions(+), 40 deletions(-)
--- a/Include/pythonrun.h
+++ b/Include/pythonrun.h
@@ -108,6 +108,8 @@ PyAPI_FUNC(char *) Py_GetPath(void);
/* In their own files */
PyAPI_FUNC(const char *) Py_GetVersion(void);
PyAPI_FUNC(const char *) Py_GetPlatform(void);
+PyAPI_FUNC(const char *) Py_GetArch(void);
+PyAPI_FUNC(const char *) Py_GetLib(void);
PyAPI_FUNC(const char *) Py_GetCopyright(void);
PyAPI_FUNC(const char *) Py_GetCompiler(void);
PyAPI_FUNC(const char *) Py_GetBuildInfo(void);
--- a/Lib/distutils/command/install.py
+++ b/Lib/distutils/command/install.py
@@ -22,6 +22,8 @@ from site import USER_BASE
from site import USER_SITE
+libname = sys.lib
+
if sys.version < "2.2":
WINDOWS_SCHEME = {
'purelib': '$base',
@@ -42,7 +44,7 @@ else:
INSTALL_SCHEMES = {
'unix_prefix': {
'purelib': '$base/lib/python$py_version_short/site-packages',
- 'platlib': '$platbase/lib/python$py_version_short/site-packages',
+ 'platlib': '$platbase/'+libname+'/python$py_version_short/site-packages',
'headers': '$base/include/python$py_version_short/$dist_name',
'scripts': '$base/bin',
'data' : '$base',
--- a/Lib/distutils/sysconfig.py
+++ b/Lib/distutils/sysconfig.py
@@ -129,8 +129,11 @@ def get_python_lib(plat_specific=0, stan
prefix = plat_specific and EXEC_PREFIX or PREFIX
if os.name == "posix":
- libpython = os.path.join(prefix,
- "lib", "python" + get_python_version())
+ if plat_specific or standard_lib:
+ lib = sys.lib
+ else:
+ lib = "lib"
+ libpython = os.path.join(prefix, lib, "python" + get_python_version())
if standard_lib:
return libpython
else:
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -375,7 +375,7 @@ class Doc:
docmodule = docclass = docroutine = docother = docproperty = docdata = fail
def getdocloc(self, object,
- basedir=os.path.join(sys.exec_prefix, "lib",
+ basedir=os.path.join(sys.exec_prefix, sys.lib,
"python"+sys.version[0:3])):
"""Return the location of module docs or None"""
--- a/Lib/site.py
+++ b/Lib/site.py
@@ -231,29 +231,38 @@ def getuserbase():
USER_BASE = get_config_var('userbase')
return USER_BASE
-def getusersitepackages():
+def getusersitepackages(lib_kind = 'purelib'):
"""Returns the user-specific site-packages directory path.
If the global variable ``USER_SITE`` is not initialized yet, this
function will also set it.
"""
+
+ set_user_site = (lib_kind == 'purelib')
+
global USER_SITE
user_base = getuserbase() # this will also set USER_BASE
- if USER_SITE is not None:
+ if USER_SITE is not None and set_user_site:
return USER_SITE
from sysconfig import get_path
import os
+ user_site = None
+
if sys.platform == 'darwin':
from sysconfig import get_config_var
if get_config_var('PYTHONFRAMEWORK'):
- USER_SITE = get_path('purelib', 'osx_framework_user')
- return USER_SITE
+ user_site = get_path(lib_kind, 'osx_framework_user')
- USER_SITE = get_path('purelib', '%s_user' % os.name)
- return USER_SITE
+ if user_site is None:
+ user_site = get_path(lib_kind, '%s_user' % os.name)
+
+ if set_user_site:
+ USER_SITE = user_site
+
+ return user_site
def addusersitepackages(known_paths):
"""Add a per user site-package to sys.path
@@ -263,10 +272,12 @@ def addusersitepackages(known_paths):
"""
# get the per user site-package path
# this call will also make sure USER_BASE and USER_SITE are set
- user_site = getusersitepackages()
+ for kind in ('purelib', 'platlib'):
+ user_site = getusersitepackages(kind)
+
+ if ENABLE_USER_SITE and os.path.isdir(user_site):
+ addsitedir(user_site, known_paths)
- if ENABLE_USER_SITE and os.path.isdir(user_site):
- addsitedir(user_site, known_paths)
return known_paths
def getsitepackages():
@@ -288,13 +299,18 @@ def getsitepackages():
if sys.platform in ('os2emx', 'riscos'):
sitepackages.append(os.path.join(prefix, "Lib", "site-packages"))
elif os.sep == '/':
- sitepackages.append(os.path.join(prefix, "lib",
+ sitepackages.append(os.path.join(prefix, sys.lib,
"python" + sys.version[:3],
"site-packages"))
- sitepackages.append(os.path.join(prefix, "lib", "site-python"))
+ sitepackages.append(os.path.join(prefix, sys.lib, "site-python"))
+ if sys.lib != "lib":
+ sitepackages.append(os.path.join(prefix, "lib",
+ "python" + sys.version[:3],
+ "site-packages"))
+ sitepackages.append(os.path.join(prefix, "lib", "site-python"))
else:
sitepackages.append(prefix)
- sitepackages.append(os.path.join(prefix, "lib", "site-packages"))
+ sitepackages.append(os.path.join(prefix, sys.lib, "site-packages"))
return sitepackages
def addsitepackages(known_paths):
--- a/Lib/sysconfig.py
+++ b/Lib/sysconfig.py
@@ -7,10 +7,10 @@ from os.path import pardir, realpath
_INSTALL_SCHEMES = {
'posix_prefix': {
- 'stdlib': '{base}/lib/python{py_version_short}',
- 'platstdlib': '{platbase}/lib/python{py_version_short}',
+ 'stdlib': '{base}/'+sys.lib+'/python{py_version_short}',
+ 'platstdlib': '{platbase}/'+sys.lib+'/python{py_version_short}',
'purelib': '{base}/lib/python{py_version_short}/site-packages',
- 'platlib': '{platbase}/lib/python{py_version_short}/site-packages',
+ 'platlib': '{platbase}/'+sys.lib+'/python{py_version_short}/site-packages',
'include': '{base}/include/python{py_version_short}',
'platinclude': '{platbase}/include/python{py_version_short}',
'scripts': '{base}/bin',
@@ -65,10 +65,10 @@ _INSTALL_SCHEMES = {
'data' : '{userbase}',
},
'posix_user': {
- 'stdlib': '{userbase}/lib/python{py_version_short}',
- 'platstdlib': '{userbase}/lib/python{py_version_short}',
+ 'stdlib': '{userbase}/'+sys.lib+'/python{py_version_short}',
+ 'platstdlib': '{userbase}/'+sys.lib+'/python{py_version_short}',
'purelib': '{userbase}/lib/python{py_version_short}/site-packages',
- 'platlib': '{userbase}/lib/python{py_version_short}/site-packages',
+ 'platlib': '{userbase}/'+sys.lib+'/python{py_version_short}/site-packages',
'include': '{userbase}/include/python{py_version_short}',
'scripts': '{userbase}/bin',
'data' : '{userbase}',
--- a/Lib/test/test_dl.py
+++ b/Lib/test/test_dl.py
@@ -4,10 +4,11 @@
import unittest
from test.test_support import verbose, import_module
dl = import_module('dl', deprecated=True)
+import sys
sharedlibs = [
- ('/usr/lib/libc.so', 'getpid'),
- ('/lib/libc.so.6', 'getpid'),
+ ('/usr/'+sys.lib+'/libc.so', 'getpid'),
+ ('/'+sys.lib+'/libc.so.6', 'getpid'),
('/usr/bin/cygwin1.dll', 'getpid'),
('/usr/lib/libc.dylib', 'getpid'),
]
--- a/Lib/test/test_site.py
+++ b/Lib/test/test_site.py
@@ -254,12 +254,16 @@ class HelperFunctionsTests(unittest.Test
self.assertEqual(dirs[0], wanted)
elif os.sep == '/':
# OS X, Linux, FreeBSD, etc
- self.assertEqual(len(dirs), 2)
wanted = os.path.join('xoxo', 'lib', 'python' + sys.version[:3],
'site-packages')
- self.assertEqual(dirs[0], wanted)
+ self.assertTrue(wanted in dirs)
wanted = os.path.join('xoxo', 'lib', 'site-python')
- self.assertEqual(dirs[1], wanted)
+ self.assertTrue(wanted in dirs)
+ wanted = os.path.join('xoxo', sys.lib, 'python' + sys.version[:3],
+ 'site-packages')
+ self.assertTrue(wanted in dirs)
+ wanted = os.path.join('xoxo', sys.lib, 'site-python')
+ self.assertTrue(wanted in dirs)
else:
# other platforms
self.assertEqual(len(dirs), 2)
@@ -305,7 +309,11 @@ class HelperFunctionsTests(unittest.Test
known_paths = set()
site.addusersitepackages(known_paths)
- self.assertEqual(fake_isdir.arg, user_site)
+ # value of user_site cannot be used for comparison, because
+ # the following assert mistakenly assumes 'platlib' and
+ # 'purelib' directories are same.
+ self.assertEqual(fake_isdir.arg,
+ site.getusersitepackages('platlib'))
self.assertFalse(known_paths)
--- a/Lib/trace.py
+++ b/Lib/trace.py
@@ -754,10 +754,10 @@ def main(argv=None):
# should I also call expanduser? (after all, could use $HOME)
s = s.replace("$prefix",
- os.path.join(sys.prefix, "lib",
+ os.path.join(sys.prefix, sys.lib,
"python" + sys.version[:3]))
s = s.replace("$exec_prefix",
- os.path.join(sys.exec_prefix, "lib",
+ os.path.join(sys.exec_prefix, sys.lib,
"python" + sys.version[:3]))
s = os.path.normpath(s)
ignore_dirs.append(s)
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -91,6 +91,8 @@ PY_CFLAGS= $(CFLAGS) $(CPPFLAGS) $(CFLAG
# Machine-dependent subdirectories
MACHDEP= @MACHDEP@
+LIB= @LIB@
+ARCH= @ARCH@
# Multiarch directory (may be empty)
MULTIARCH= @MULTIARCH@
@@ -653,6 +655,7 @@ Modules/getpath.o: $(srcdir)/Modules/get
-DEXEC_PREFIX='"$(exec_prefix)"' \
-DVERSION='"$(VERSION)"' \
-DVPATH='"$(VPATH)"' \
+ -DARCH='"$(ARCH)"' -DLIB='"$(LIB)"' \
-o $@ $(srcdir)/Modules/getpath.c
Modules/python.o: $(srcdir)/Modules/python.c
@@ -701,7 +704,7 @@ regen-ast:
Python/compile.o Python/symtable.o Python/ast.o: $(srcdir)/Include/graminit.h $(srcdir)/Include/Python-ast.h
Python/getplatform.o: $(srcdir)/Python/getplatform.c
- $(CC) -c $(PY_CFLAGS) -DPLATFORM='"$(MACHDEP)"' -o $@ $(srcdir)/Python/getplatform.c
+ $(CC) -c $(PY_CFLAGS) -DPLATFORM='"$(MACHDEP)"' -DARCH='"$(ARCH)"' -DLIB='"$(LIB)"' -o $@ $(srcdir)/Python/getplatform.c
Python/importdl.o: $(srcdir)/Python/importdl.c
$(CC) -c $(PY_CFLAGS) -I$(DLINCLDIR) -o $@ $(srcdir)/Python/importdl.c
--- a/Modules/getpath.c
+++ b/Modules/getpath.c
@@ -100,6 +100,8 @@
#error "PREFIX, EXEC_PREFIX, VERSION, and VPATH must be constant defined"
#endif
+#define LIB_PYTHON LIB "/python" VERSION
+
#ifndef LANDMARK
#define LANDMARK "os.py"
#endif
@@ -108,7 +110,7 @@ static char prefix[MAXPATHLEN+1];
static char exec_prefix[MAXPATHLEN+1];
static char progpath[MAXPATHLEN+1];
static char *module_search_path = NULL;
-static char lib_python[] = "lib/python" VERSION;
+static char lib_python[] = LIB_PYTHON;
static void
reduce(char *dir)
--- a/Python/getplatform.c
+++ b/Python/getplatform.c
@@ -10,3 +10,23 @@ Py_GetPlatform(void)
{
return PLATFORM;
}
+
+#ifndef ARCH
+#define ARCH "unknown"
+#endif
+
+const char *
+Py_GetArch(void)
+{
+ return ARCH;
+}
+
+#ifndef LIB
+#define LIB "lib"
+#endif
+
+const char *
+Py_GetLib(void)
+{
+ return LIB;
+}
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1437,6 +1437,10 @@ _PySys_Init(void)
PyString_FromString(Py_GetCopyright()));
SET_SYS_FROM_STRING("platform",
PyString_FromString(Py_GetPlatform()));
+ SET_SYS_FROM_STRING("arch",
+ PyString_FromString(Py_GetArch()));
+ SET_SYS_FROM_STRING("lib",
+ PyString_FromString(Py_GetLib()));
SET_SYS_FROM_STRING("executable",
PyString_FromString(Py_GetProgramFullPath()));
SET_SYS_FROM_STRING("prefix",
--- a/configure.ac
+++ b/configure.ac
@@ -773,6 +773,41 @@ SunOS*)
;;
esac
+AC_SUBST(ARCH)
+AC_MSG_CHECKING(ARCH)
+ARCH=`uname -m`
+case $ARCH in
+i?86) ARCH=i386;;
+esac
+AC_MSG_RESULT($ARCH)
+
+AC_SUBST(LIB)
+AC_MSG_CHECKING(LIB)
+case $ac_sys_system in
+Linux*)
+ # Test if the compiler is 64bit
+ echo 'int i;' > conftest.$ac_ext
+ python_cv_cc_64bit_output=no
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *"ELF 64"*)
+ python_cv_cc_64bit_output=yes
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+case $ARCH:$python_cv_cc_64bit_output in
+aarch64:yes | ppc64:yes | ppc64le:yes | powerpc64:yes | riscv64:yes | s390x:yes | sparc64:yes | x86_64:yes)
+ LIB="lib64"
+ ;;
+*:*)
+ LIB="lib"
+ ;;
+esac
+AC_MSG_RESULT($LIB)
AC_SUBST(LIBRARY)
AC_MSG_CHECKING(LIBRARY)
--- a/setup.py
+++ b/setup.py
@@ -502,7 +502,7 @@ class PyBuildExt(build_ext):
def detect_modules(self):
# Ensure that /usr/local is always used
if not cross_compiling:
- add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib')
+ add_dir_to_list(self.compiler.library_dirs, '/usr/local/' + sys.lib)
add_dir_to_list(self.compiler.include_dirs, '/usr/local/include')
if cross_compiling:
self.add_gcc_paths()
@@ -828,11 +828,11 @@ class PyBuildExt(build_ext):
elif curses_library:
readline_libs.append(curses_library)
elif self.compiler.find_library_file(lib_dirs +
- ['/usr/lib/termcap'],
+ ['/usr/'+sys.lib+'/termcap'],
'termcap'):
readline_libs.append('termcap')
exts.append( Extension('readline', ['readline.c'],
- library_dirs=['/usr/lib/termcap'],
+ library_dirs=['/usr/'+sys.lib+'/termcap'],
extra_link_args=readline_extra_link_args,
libraries=readline_libs) )
else:
@@ -1979,18 +1979,17 @@ class PyBuildExt(build_ext):
# Check for various platform-specific directories
if host_platform == 'sunos5':
include_dirs.append('/usr/openwin/include')
- added_lib_dirs.append('/usr/openwin/lib')
+ added_lib_dirs.append('/usr/openwin/' + sys.lib)
elif os.path.exists('/usr/X11R6/include'):
include_dirs.append('/usr/X11R6/include')
- added_lib_dirs.append('/usr/X11R6/lib64')
- added_lib_dirs.append('/usr/X11R6/lib')
+ added_lib_dirs.append('/usr/X11R6/' + sys.lib)
elif os.path.exists('/usr/X11R5/include'):
include_dirs.append('/usr/X11R5/include')
- added_lib_dirs.append('/usr/X11R5/lib')
+ added_lib_dirs.append('/usr/X11R5/' + sys.lib)
else:
# Assume default location for X11
include_dirs.append('/usr/X11/include')
- added_lib_dirs.append('/usr/X11/lib')
+ added_lib_dirs.append('/usr/X11/' + sys.lib)
# If Cygwin, then verify that X is installed before proceeding
if host_platform == 'cygwin':

View File

@ -0,0 +1,25 @@
---
Lib/ssl.py | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -497,7 +497,17 @@ def _get_https_context_factory():
return _create_unverified_context
return create_default_context
-_create_default_https_context = _get_https_context_factory()
+try:
+ # load the TLS checks policy from separate package
+ import sle_tls_checks_policy as policy
+ if policy.get_policy:
+ _create_default_https_context = policy.get_policy()
+ else:
+ # empty policy file means simply enable strict verification
+ _create_default_https_context = _get_https_context_factory()
+except ImportError:
+ # policy not present, disable verification for backwards compatibility
+ _create_default_https_context = _create_unverified_context
# PEP 493: "private" API to configure HTTPS defaults without monkeypatching
def _https_verify_certificates(enable=True):

View File

@ -0,0 +1,19 @@
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -537,7 +537,15 @@ class SSLSocket(socket):
self._context = SSLContext(ssl_version)
self._context.verify_mode = cert_reqs
if ca_certs:
- self._context.load_verify_locations(ca_certs)
+ capath = None
+ cafile = None
+ if os.path.isdir(ca_certs):
+ capath = ca_certs
+ else:
+ cafile = ca_certs
+ self._context.load_verify_locations(cafile=cafile, capath=capath)
+ elif cert_reqs != CERT_NONE:
+ self._context.set_default_verify_paths()
if certfile:
self._context.load_cert_chain(certfile, keyfile)
if npn_protocols:

3
python-base-rpmlintrc Normal file
View File

@ -0,0 +1,3 @@
addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/distutils/tests/xxmodule.c")
addFilter("devel-file-in-non-devel-package.*/usr/include/python.*/pyconfig.h")
addFilter("executable-stack.*/usr/lib/python2.7/lib-dynload/_ctypes.so")

2720
python-base.changes Normal file

File diff suppressed because it is too large Load Diff

641
python-base.spec Normal file
View File

@ -0,0 +1,641 @@
#
# spec file for package python-base
#
# Copyright (c) 2024 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
%define so_version 2_7-1_0
# We really don't care about quality of this package anymore, it
# will be soon gone (bsc#1219306).
%bcond_with test
Name: python-base
Version: 2.7.18
Release: 0
Summary: Python Interpreter base package
License: Python-2.0
Group: Development/Languages/Python
URL: https://www.python.org/
%define tarversion %{version}
%define tarname Python-%{tarversion}
Source0: https://www.python.org/ftp/python/%{version}/%{tarname}.tar.xz
Source4: https://www.python.org/ftp/python/%{version}/%{tarname}.tar.xz.asc
Source6: python.keyring
Source1: macros.python2
Source2: baselibs.conf
Source3: README.SUSE
Source5: local.pth
# Fixed bundled wheels
Source10: setuptools-44.1.1-py2.py3-none-any.whl
Source11: pip-20.0.2-py2.py3-none-any.whl
# For Patch 66
Source66: recursion.tar
Source99: python-base-rpmlintrc
# COMMON-PATCH-BEGIN
Patch1: python-2.7-dirs.patch
Patch2: python-distutils-rpm-8.patch
Patch3: python-2.7.5-multilib.patch
Patch4: python-2.5.1-sqlite.patch
Patch5: python-2.7.4-canonicalize2.patch
Patch7: python-2.6-gettext-plurals.patch
Patch8: python-2.6b3-curses-panel.patch
Patch13: python-2.7.2-fix_date_time_compiler.patch
Patch17: remove-static-libpython.patch
# PATCH-FEATURE-OPENSUSE python-bundle-lang.patch bnc#617751 dimstar@opensuse.org -- gettext: when looking in default_localedir also check in locale-bundle.
Patch20: python-bundle-lang.patch
Patch24: python-bsddb6.patch
# PATCH-FIX-UPSTREAM accept directory-based CA paths as well
Patch33: python-2.7.9-ssl_ca_path.patch
# PATCH-FEATURE-SLE disable SSL verification-by-default in http clients
Patch34: python-2.7.9-sles-disable-verification-by-default.patch
# PATCH-FIX-UPSTREAM do not use non-ASCII filename in test_ssl.py
Patch35: do-not-use-non-ascii-in-test_ssl.patch
# PATCH-FIX-UPSTREAM bmwiedemann@suse.de -- allow python packages to build reproducibly
Patch38: reproducible.patch
# bypass boo#1078485 random failing tests
Patch40: python-skip_random_failing_tests.patch
# PATCH-FIX-UPSTREAM sorted tar https://github.com/python/cpython/pull/2263
Patch41: python-sorted_tar.patch
# https://github.com/python/cpython/pull/9624 (https://bugs.python.org/issue34834)
Patch47: openssl-111-middlebox-compat.patch
# PATCH-FIX-SUSE python default SSLContext doesn't contain OP_CIPHER_SERVER_PREFERENCE
Patch48: openssl-111-ssl_options.patch
# PATCH-FIX-UPSTREAM CVE-2019-5010-null-defer-x509-cert-DOS.patch bnc#1122191 mcepl@suse.com
# gh#python/cpython#11569
# Fix segfault in ssl's cert parser
Patch49: CVE-2019-5010-null-defer-x509-cert-DOS.patch
# PATCH-FIX-UPSTREAM bpo36160-init-sysconfig_vars.patch gh#python/cpython#12131 mcepl@suse.com
# Initialize sysconfig variables in test_site.
Patch50: bpo36160-init-sysconfig_vars.patch
# PATCH-FIX-UPSTREAM CVE-2017-18207.patch gh#python/cpython#4437 psimons@suse.com
# Add check for channels of wav file in Lib/wave.py
Patch51: CVE-2017-18207.patch
# PATCH-FIX-UPSTREAM gh#python/cpython#12341
Patch55: bpo36302-sort-module-sources.patch
# Fix installation in /usr/local (boo#1071941), adapted from Fedora
# https://src.fedoraproject.org/rpms/python3/blob/master/f/00251-change-user-install-location.patch
# Set values of prefix and exec_prefix in distutils install command
# to /usr/local if executable is /usr/bin/python* and RPM build
# is not detected to make pip and distutils install into separate location
Patch56: adapted-from-F00251-change-user-install-location.patch
# Switch couple of tests failing on acient SLE-12
Patch57: python-2.7.17-switch-off-failing-SSL-tests.patch
# PATCH-FIX-UPSTREAM CVE-2020-8492-urllib-ReDoS.patch bsc#1162367 mcepl@suse.com
# Fixes Python urrlib allowed an HTTP server to conduct Regular
# Expression Denial of Service (ReDoS)
Patch58: CVE-2020-8492-urllib-ReDoS.patch
# PATCH-FIX-UPSTREAM CVE-2019-9674-zip-bomb.patch bsc#1162825 mcepl@suse.com
# Improve documentation warning against the possible zip bombs
Patch59: CVE-2019-9674-zip-bomb.patch
# PATCH-FIX-UPSTREAM configure_PYTHON_FOR_REGEN.patch bsc#1078326 mcepl@suse.com
# PYTHON_FOR_REGEN value is set very weird upstream
Patch60: configure_PYTHON_FOR_REGEN.patch
# PATCH-FIX-SLE CVE-2021-3177-buf_ovrfl_PyCArg_repr.patch bsc#1181126 mcepl@suse.com
# buffer overflow in PyCArg_repr in _ctypes/callproc.c, which may lead to remote code execution
Patch61: CVE-2021-3177-buf_ovrfl_PyCArg_repr.patch
# PATCH-FIX-UPSTREAM CVE-2021-23336-only-amp-as-query-sep.patch bsc#[0-9]+ mcepl@suse.com
# this patch makes things totally awesome
Patch62: CVE-2021-23336-only-amp-as-query-sep.patch
# PATCH-FIX-UPSTREAM CVE-2021-3737-fix-HTTP-client-infinite-line-reading-after-a-HTTP-100-Continue.patch boo#1189241 gh#python/cpython#25916
Patch63: CVE-2021-3737-fix-HTTP-client-infinite-line-reading-after-a-HTTP-100-Continue.patch
# PATCH-FIX-UPSTREAM CVE-2021-3733-fix-ReDoS-in-request.patch boo#1189287 gh#python/cpython#24391
Patch64: CVE-2021-3733-fix-ReDoS-in-request.patch
# PATCH-FIX-UPSTREAM sphinx-update-removed-function.patch bpo#35293 gh#python/cpython#22198 -- fix doc build
Patch65: sphinx-update-removed-function.patch
# PATCH-FIX-UPSTREAM CVE-2019-20907_tarfile-inf-loop.patch bsc#1174091 mcepl@suse.com
# avoid possible infinite loop in specifically crafted tarball (CVE-2019-20907)
# REQUIRES SOURCE 66
Patch66: CVE-2019-20907_tarfile-inf-loop.patch
# PATCH-FIX-UPSTREAM CVE-2020-26116-httplib-header-injection.patch bsc#1177211
# Fixes httplib to disallow control characters in method to avoid header
# injection
Patch67: CVE-2020-26116-httplib-header-injection.patch
# PATCH-FIX-UPSTREAM CVE-2021-4189-ftplib-trust-PASV-resp.patch bsc#1194146 mcepl@suse.com
# Make ftplib not trust the PASV response. (gh#python/cpython#24838)
Patch68: CVE-2021-4189-ftplib-trust-PASV-resp.patch
# PATCH-FIX-UPSTREAM CVE-2022-0391-urllib_parse-newline-parsing.patch bsc#1195396 mcepl@suse.com
# whole long discussion is on bpo#43882
# fix for santization URLs containing ASCII newline and tabs in urllib.parse
Patch69: CVE-2022-0391-urllib_parse-newline-parsing.patch
# PATCH-FIX-UPSTREAM CVE-2015-20107-mailcap-unsafe-filenames.patch bsc#1198511 mcepl@suse.com
# avoid the command injection in the mailcap module.
Patch70: CVE-2015-20107-mailcap-unsafe-filenames.patch
# PATCH-FIX-UPSTREAM CVE-2021-28861 bsc#1202624
# Coerce // to / in Lib/BaseHTTPServer.py
Patch71: CVE-2021-28861-double-slash-path.patch
Patch72: bpo34990-2038-problem-compileall.patch
# PATCH-FIX-UPSTREAM CVE-2022-45061-DoS-by-IDNA-decode.patch bsc#1205244 mcepl@suse.com
# Avoid DoS by decoding IDNA for too long domain names
Patch73: CVE-2022-45061-DoS-by-IDNA-decode.patch
# PATCH-FIX-UPSTREAM skip_unverified_test.patch mcepl@suse.com
# switching verification off on the old SLE doesn't work
Patch74: skip_unverified_test.patch
# PATCH-FIX-UPSTREAM CVE-2023-24329-blank-URL-bypass.patch bsc#1208471 mcepl@suse.com
# blocklist bypass via the urllib.parse component when supplying
# a URL that starts with blank characters
Patch75: CVE-2023-24329-blank-URL-bypass.patch
# PATCH-FIX-OPENSUSE PygmentsBridge-trime_doctest_flags.patch mcepl@suse.com
# Build documentation even without PygmentsBridge.trim_doctest_flags
Patch76: PygmentsBridge-trime_doctest_flags.patch
# PATCH-FIX-UPSTREAM CVE-2023-27043-email-parsing-errors.patch bsc#1210638 mcepl@suse.com
# Detect email address parsing errors and return empty tuple to
# indicate the parsing error (old API), modified for fixing bsc#1222537,
# so that email.utils.parseaddr accepts unicode string
Patch77: CVE-2023-27043-email-parsing-errors.patch
# PATCH-FIX-UPSTREAM CVE-2022-48565-plistlib-XML-vulns.patch bsc#1214685 mcepl@suse.com
# Reject entity declarations in plists
Patch78: CVE-2022-48565-plistlib-XML-vulns.patch
# PATCH-FIX-UPSTREAM CVE-2023-40217-avoid-ssl-pre-close.patch gh#python/cpython#108315
Patch79: CVE-2023-40217-avoid-ssl-pre-close.patch
# PATCH-FIX-UPSTREAM CVE-2022-48566-compare_digest-more-constant.patch bsc#1214691 mcepl@suse.com
# Make compare_digest more constant-time
Patch80: CVE-2022-48566-compare_digest-more-constant.patch
# PATCH-FIX-OPENSUSE CVE-2023-52425-libexpat-2.6.0-remove-failing-tests.patch bpo#3151 mcepl@suse.com
# We don't have fix for bpo#3151 and it is just not supported
Patch81: CVE-2023-52425-libexpat-2.6.0-remove-failing-tests.patch
# PATCH-FIX-UPSTREAM CVE-2024-0450-zipfile-avoid-quoted-overlap-zipbomb.patch bsc#1221854 mcepl@suse.com
# detecting the vulnerability of the "quoted-overlap" zipbomb (from gh#python/cpython!110016).
Patch82: CVE-2024-0450-zipfile-avoid-quoted-overlap-zipbomb.patch
# COMMON-PATCH-END
%define python_version %(echo %{tarversion} | head -c 3)
BuildRequires: automake
BuildRequires: fdupes
BuildRequires: libbz2-devel
BuildRequires: libexpat-devel
BuildRequires: libffi-devel
# This is NOT switching off NIS support on SLE < 15,
# support for NIS used to be in the glibc itself
%if 0%{?suse_version} >= 1500 && 0%{?suse_version} < 1599
BuildRequires: libnsl-devel
%endif
BuildRequires: pkg-config
%if 0%{?suse_version} >= 1550
# The provider for python(abi) is in rpm-build-python
BuildRequires: rpm-build-python
%endif
BuildRequires: xz
BuildRequires: zlib-devel
#!BuildIgnore: python
# for the test suite
BuildRequires: netcfg
# explicitly, see bnc#697251:
Requires: libpython%{so_version} = %{version}-%{release}
Provides: %{name} = %{python_version}
# bug437293
%ifarch ppc64
Obsoletes: python-64bit
%endif
Provides: python-ctypes = 1.1.0
Obsoletes: python-ctypes < 1.1.0
Provides: python-argparse = 1.4.0.1
Obsoletes: python-argparse < 1.4.0.1
Provides: python2-argparse = 1.4.0.1
BuildRoot: %{_tmppath}/%{name}-%{version}-build
Provides: python2-base = %{version}
%description
Python is an interpreted, object-oriented programming language, and is
often compared to Tcl, Perl, Scheme, or Java. You can find an overview
of Python in the documentation and tutorials included in the python-doc
(HTML) or python-doc-pdf (PDF) packages.
This package contains all of stand-alone Python files, minus binary
modules that would pull in extra dependencies.
%package -n python-devel
Summary: Include Files and Libraries Mandatory for Building Python Modules
Group: Development/Languages/Python
Requires: glibc-devel
Requires: python = %{version}
Requires: python-base = %{version}-%{release}
Provides: python2-devel = %{version}
# provide testsuite namespace that was split in python3 to ease dependencies
Provides: python-testsuite = %{version}
Provides: python2-testsuite = %{version}
%description -n python-devel
The Python programming language's interpreter can be extended with
dynamically loaded extensions and can be embedded in other programs.
This package contains header files, a static library, and development
tools for building Python modules, extending the Python interpreter or
embedding Python in applications.
%package -n python-xml
Summary: A Python XML Interface
Group: Development/Libraries/Python
Requires: python-base = %{version}-%{release}
# pyxml used to live out of tree
Provides: pyxml = 0.8.5
Obsoletes: pyxml < 0.8.5
Provides: python2-xml = %{version}
%description -n python-xml
The expat module is a Python interface to the expat XML parser. Since
Python2.x, it is part of the core Python distribution.
%package -n libpython%{so_version}
Summary: Python Interpreter shared library
Group: Development/Languages/Python
%description -n libpython2_7-1_0
Python is an interpreted, object-oriented programming language, and is
often compared to Tcl, Perl, Scheme, or Java. You can find an overview
of Python in the documentation and tutorials included in the python-doc
(HTML) or python-doc-pdf (PDF) packages.
This package contains libpython2.7 shared library for embedding in
other applications.
%prep
%setup -q -n %{tarname}
# patching
# COMMON-PREP-BEGIN
%patch -P 1 -p1
%patch -P 2 -p1
%patch -P 3 -p1
%patch -P 4 -p1
%patch -P 5 -p1
%patch -P 7 -p1
%patch -P 8 -p1
%patch -P 13 -p1
%patch -P 17 -p1
%patch -P 20 -p1
%patch -P 24 -p1
%patch -P 33 -p1
%if %{suse_version} < 1500 && !0%{?is_opensuse}
%patch -P 34 -p1
%endif
%patch -P 35 -p1
%patch -P 38 -p1
%ifarch ppc ppc64 ppc64le
%patch -P 40 -p1
%endif
%patch -P 41 -p1
%if %{suse_version} >= 1500 || (0%{?sle_version} && 0%{?sle_version} >= 120400)
%patch -P 47 -p1
%patch -P 48 -p1
%endif
# SLE-12 needs to skip more
%if %{suse_version} == 1315
%patch -P 57 -p1
%endif
%patch -P 49 -p1
%patch -P 50 -p1
%patch -P 51 -p1
%patch -P 55 -p1
%patch -P 56 -p1
%patch -P 58 -p1
%patch -P 59 -p1
%patch -P 60 -p1
%patch -P 61 -p1
%patch -P 62 -p1
%patch -P 63 -p1
%patch -P 64 -p1
%patch -P 65 -p1
%patch -P 66 -p1
%patch -P 67 -p1
%patch -P 68 -p1
%patch -P 69 -p1
%patch -P 70 -p1
%patch -P 71 -p1
%patch -P 72 -p1
%patch -P 73 -p1
%if 0%{?sle_version} && 0%{?sle_version} < 150000
%patch -P 74 -p1
%endif
%patch -P 75 -p1
%patch -P 76 -p1
%patch -P 77 -p1
%patch -P 78 -p1
%patch -P 79 -p1
%patch -P 80 -p1
%patch -P 81 -p1
%patch -P 82 -p1
# For patch 66
cp -v %{SOURCE66} Lib/test/recursion.tar
# drop Autoconf version requirement
sed -i 's/^version_required/dnl version_required/' configure.ac
# COMMON-PREP-END
# Replace bundled wheels with the updates ones
rm -v Lib/ensurepip/_bundled/*.whl
cp -v %{SOURCE10} %{SOURCE11} Lib/ensurepip/_bundled/
STVER=$(basename %{SOURCE10}|cut -d- -f2)
PIPVER=$(basename %{SOURCE11}|cut -d- -f2)
sed -i -e "s/^\(\s*_SETUPTOOLS_VERSION\s\+=\s\+\)\"[0-9.]\+\"/\1\"${STVER}\"/" \
-e "s/^\(\s*_PIP_VERSION\s\+=\s\+\)\"[0-9.]\+\"/\1\"${PIPVER}\"/" \
Lib/ensurepip/__init__.py
cp -p %{SOURCE1} macros.python2
%if %{suse_version} < 1500
# on SLE12 and SLE11 the python2 modules will still be called python-xxxx
# as this SPEC file is used on SLE12, keep it in here for the time being
sed -i -e 's/python2_package_prefix python2/python2_package_prefix python/' macros.python2
%endif
# Ensure that we're using the system copy of various libraries, rather than
# copies shipped by upstream in the tarball:
# Remove embedded copy of expat:
rm -r Modules/expat || exit 1
# Remove embedded copy of libffi:
for SUBDIR in darwin libffi libffi_arm_wince libffi_msvc libffi_osx ; do
rm -r Modules/_ctypes/$SUBDIR || exit 1 ;
done
# Remove embedded copy of zlib:
rm -r Modules/zlib || exit 1
%build
%define _lto_cflags %{nil}
# -std=gnu89 option is needed to build with gcc14, bsc#1220970
export OPT="%{optflags} -DOPENSSL_LOAD_CONF -fwrapv -std=gnu89"
autoreconf -f -i . # Modules/_ctypes/libffi
# provide a stable timestamp
touch -r %{SOURCE0} Makefile.pre.in
# prevent make from trying to rebuild asdl stuff, which requires existing
# python installation
touch Parser/asdl* Python/Python-ast.c Include/Python-ast.h
%configure \
--docdir=%{_docdir}/python \
--with-fpectl \
--with-system-ffi \
--with-system-expat \
--enable-ipv6 \
--enable-shared \
--enable-unicode=ucs4
%if 0%{?do_profiling}
target=profile-opt
%else
target=all
%endif
LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH \
make %{?_smp_mflags} $target
%check
%if %{with test}
# on hppa, the threading of glibc is quite broken. The tests just stop
# at some point, and the machine does not build anything more until a
# timeout several hours later.
%ifnarch hppa
# test_file(2k) fails in autobuild env - "stdin.seek(-1)" wrongly succeeds. probably an issue with autobuild's stdin
# test_urllib2 relies on being able to resolve local address, which is notoriously impossible in autobuild
# test_urllib2_localnet randomly fails out
EXCLUDE="test_urllib2 test_urllib2_localnet test_file test_file2k"
# test_nis and test_threading are AWFULLY slow.
EXCLUDE="$EXCLUDE test_nis test_threading"
# test_gdb fails if gdb with (different) python support is part of the buildsystem
EXCLUDE="$EXCLUDE test_gdb"
%ifarch ia64
# test_smtplib's testSend is known to be broken and on ia64 it actually fails most of the time, preventing the build.
EXCLUDE="$EXCLUDE test_smtplib"
%endif
# test_unicode fails in Factory
EXCLUDE="$EXCLUDE test_unicode"
%if 0%{?qemu_user_space_build}
# test_asyncore fails because of unimplemented sockopt
EXCLUDE="$EXCLUDE test_asyncore test_mmap"
# emulation is unreliable
EXCLUDE="$EXCLUDE test_multiprocessing test_thread"
# qemu bug (siginterrupt handling)
EXCLUDE="$EXCLUDE test_signal"
%endif
%ifarch s390 s390x
# test_regrtest tries to segfault the interpreter by dereferencing a NULL pointer, but that doesn't
# actually produce a segfault on S390
EXCLUDE="$EXCLUDE test_regrtest"
%endif
# This test (part of test_uuid) requires real network interfaces
# so that ifconfig output has "HWaddr <something>". Some kvm instances
# don't have any such interface breaking the uuid module test.
EXCLUDE="$EXCLUDE test_uuid"
# bypass boo#1078485
# many flaky tests if osc build in loop on ppc64le
%ifarch ppc ppc64 ppc64le
EXCLUDE="$EXCLUDE test_asynchat test_asyncore test_dircache test_multiprocessing test_nntplib test_queue test_signal test_socket test_subprocess test_telnetlib test_xmlrpc "
%endif
# Limit virtual memory to avoid spurious failures
if test $(ulimit -v) = unlimited || test $(ulimit -v) -gt 10000000; then
ulimit -v 10000000 || :
fi
make test TESTOPTS="-l -w -x $EXCLUDE" TESTPYTHONOPTS="-R"
# use network, be verbose:
#make test TESTOPTS="-l -u network -v"
%endif
%endif
# END OF CHECK SECTION
%install
# replace rest of /usr/local/bin/python or /usr/bin/python2.5 with /usr/bin/python
find . -name '*.py' -type f | grep -vE "^./Parser/|^./Python/" \
| xargs grep -lE '^#! *(/usr/.*bin/(env +)?)?python' \
| xargs sed -r -i -e '1s@^#![[:space:]]*(/usr/(local/)?bin/(env +)?)?python([0-9]+\.[0-9]+)?@#!/usr/bin/python@'
# the grep inbetween makes it much faster
########################################
# install it
########################################
%make_install OPT="%{optflags} -fPIC"
install -m 644 %{SOURCE5} %{buildroot}%{_libdir}/python%{python_version}/site-packages/_local.pth
install -d -m 755 %{buildroot}%{_rpmconfigdir}/macros.d/
install -m 644 macros.python2 %{buildroot}%{_rpmconfigdir}/macros.d/
# make sure /usr/lib/python/site-packages exists even on lib64 machines
mkdir -p %{buildroot}%{_prefix}/lib/python%{python_version}/site-packages
########################################
# some cleanups
########################################
# remove hard links and replace them with symlinks
for dir in bin include %{_lib} ; do
rm -f %{buildroot}/%{_prefix}/$dir/python
ln -s python%{python_version} %{buildroot}/%{_prefix}/$dir/python
done
CLEANUP_DIR="%{buildroot}%{_libdir}/python%{python_version}"
# don't distribute precompiled windows installers (duh)
rm -f $CLEANUP_DIR/distutils/command/*.exe
# kill imageop.so - it used to be insecure and it is deprecated anyway
rm -f $CLEANUP_DIR/lib-dynload/imageop.so
# link shared library instead of static library that tools expect
ln -s ../../libpython%{python_version}.so %{buildroot}%{_libdir}/python%{python_version}/config/libpython%{python_version}.so
# remove various things that don't need to be in python-base
rm %{buildroot}%{_bindir}/idle
rm -rf $CLEANUP_DIR/{curses,bsddb,idlelib,lib-tk,sqlite3}
rm $CLEANUP_DIR/ssl.py*
# does not work without _ssl.so anyway
# replace duplicate .pyo/.pyc with hardlinks
%fdupes %{buildroot}/%{_libdir}/python%{python_version}
########################################
# documentation
########################################
export PDOCS=%{buildroot}%{_docdir}/%{name}
install -d -m 755 $PDOCS
install -c -m 644 %{SOURCE3} $PDOCS/
install -c -m 644 LICENSE $PDOCS/
install -c -m 644 README $PDOCS/
ln -s python%{python_version}.1.gz %{buildroot}%{_mandir}/man1/python.1.gz
########################################
# devel
########################################
# install Makefile.pre.in and Makefile.pre
cp Makefile Makefile.pre.in Makefile.pre %{buildroot}%{_libdir}/python%{python_version}/config/
%clean
%post -n libpython2_7-1_0 -p %{run_ldconfig}
%postun -n libpython2_7-1_0 -p %{run_ldconfig}
%files -n python-devel
%{_libdir}/python%{python_version}/config/*
%exclude %{_libdir}/python%{python_version}/config/Setup
%exclude %{_libdir}/python%{python_version}/config/Makefile
%{_libdir}/libpython*.so
%{_libdir}/pkgconfig/python-%{python_version}.pc
%{_libdir}/pkgconfig/python.pc
%{_libdir}/pkgconfig/python2.pc
%{_includedir}/python*
%exclude %{_includedir}/python%{python_version}/pyconfig.h
%{_libdir}/python%{python_version}/test
%{_bindir}/python-config
%{_bindir}/python2-config
%{_bindir}/python%{python_version}-config
%files -n python-xml
%{_libdir}/python%{python_version}/xml
%{_libdir}/python%{python_version}/lib-dynload/pyexpat.so
%files -n libpython2_7-1_0
%{_libdir}/libpython*.so.*
%files
%{_rpmconfigdir}/macros.d/macros.python2
%dir %{_docdir}/%{name}
%doc %{_docdir}/%{name}/README
%doc %{_docdir}/%{name}/LICENSE
%doc %{_docdir}/%{name}/README.SUSE
%doc %{_mandir}/man1/python.1*
%doc %{_mandir}/man1/python2.1*
%doc %{_mandir}/man1/python%{python_version}.1*
%dir %{_includedir}/python%{python_version}
%{_includedir}/python%{python_version}/pyconfig.h
%{_libdir}/python
%dir %{_prefix}/lib/python%{python_version}
%dir %{_prefix}/lib/python%{python_version}/site-packages
%dir %{_libdir}/python%{python_version}
%dir %{_libdir}/python%{python_version}/config
%{_libdir}/python%{python_version}/config/Setup
%{_libdir}/python%{python_version}/config/Makefile
%{_libdir}/python%{python_version}/*.*
%{_libdir}/python%{python_version}/compiler
%{_libdir}/python%{python_version}/ctypes
%{_libdir}/python%{python_version}/distutils
%{_libdir}/python%{python_version}/email
%{_libdir}/python%{python_version}/encodings
%{_libdir}/python%{python_version}/ensurepip
%{_libdir}/python%{python_version}/hotshot
%{_libdir}/python%{python_version}/importlib
%{_libdir}/python%{python_version}/json
%{_libdir}/python%{python_version}/lib2to3
%{_libdir}/python%{python_version}/logging
%{_libdir}/python%{python_version}/multiprocessing
%{_libdir}/python%{python_version}/plat-*
%{_libdir}/python%{python_version}/pydoc_data
%{_libdir}/python%{python_version}/unittest
%{_libdir}/python%{python_version}/wsgiref
%dir %{_libdir}/python%{python_version}/site-packages
%{_libdir}/python%{python_version}/site-packages/README
%{_libdir}/python%{python_version}/site-packages/_local.pth
%dir %{_libdir}/python%{python_version}/lib-dynload
%{_libdir}/python%{python_version}/lib-dynload/_bisect.so
#%%{_libdir}/python%%{python_version}/lib-dynload/_bytesio.so
%{_libdir}/python%{python_version}/lib-dynload/_csv.so
%{_libdir}/python%{python_version}/lib-dynload/_collections.so
%{_libdir}/python%{python_version}/lib-dynload/_ctypes.so
%{_libdir}/python%{python_version}/lib-dynload/_ctypes_test.so
%{_libdir}/python%{python_version}/lib-dynload/_elementtree.so
#%%{_libdir}/python%%{python_version}/lib-dynload/_fileio.so
%{_libdir}/python%{python_version}/lib-dynload/_functools.so
%{_libdir}/python%{python_version}/lib-dynload/_heapq.so
%{_libdir}/python%{python_version}/lib-dynload/_hotshot.so
%{_libdir}/python%{python_version}/lib-dynload/_io.so
%{_libdir}/python%{python_version}/lib-dynload/_json.so
%{_libdir}/python%{python_version}/lib-dynload/_locale.so
%{_libdir}/python%{python_version}/lib-dynload/_lsprof.so
%{_libdir}/python%{python_version}/lib-dynload/_md5.so
%{_libdir}/python%{python_version}/lib-dynload/_multiprocessing.so
%{_libdir}/python%{python_version}/lib-dynload/_random.so
%{_libdir}/python%{python_version}/lib-dynload/_sha.so
%{_libdir}/python%{python_version}/lib-dynload/_sha256.so
%{_libdir}/python%{python_version}/lib-dynload/_sha512.so
%{_libdir}/python%{python_version}/lib-dynload/_socket.so
%{_libdir}/python%{python_version}/lib-dynload/_struct.so
%{_libdir}/python%{python_version}/lib-dynload/_testcapi.so
%{_libdir}/python%{python_version}/lib-dynload/array.so
%{_libdir}/python%{python_version}/lib-dynload/audioop.so
%{_libdir}/python%{python_version}/lib-dynload/binascii.so
%{_libdir}/python%{python_version}/lib-dynload/bz2.so
%{_libdir}/python%{python_version}/lib-dynload/cPickle.so
%{_libdir}/python%{python_version}/lib-dynload/cStringIO.so
%{_libdir}/python%{python_version}/lib-dynload/cmath.so
%{_libdir}/python%{python_version}/lib-dynload/crypt.so
%{_libdir}/python%{python_version}/lib-dynload/datetime.so
%{_libdir}/python%{python_version}/lib-dynload/fcntl.so
%{_libdir}/python%{python_version}/lib-dynload/future_builtins.so
%{_libdir}/python%{python_version}/lib-dynload/grp.so
%{_libdir}/python%{python_version}/lib-dynload/itertools.so
%{_libdir}/python%{python_version}/lib-dynload/linuxaudiodev.so
%{_libdir}/python%{python_version}/lib-dynload/math.so
%{_libdir}/python%{python_version}/lib-dynload/mmap.so
%if 0%{?suse_version} >= 1315 && 0%{?suse_version} < 1599
%{_libdir}/python%{python_version}/lib-dynload/nis.so
%endif
%{_libdir}/python%{python_version}/lib-dynload/operator.so
%{_libdir}/python%{python_version}/lib-dynload/ossaudiodev.so
%{_libdir}/python%{python_version}/lib-dynload/parser.so
%{_libdir}/python%{python_version}/lib-dynload/resource.so
%{_libdir}/python%{python_version}/lib-dynload/select.so
%{_libdir}/python%{python_version}/lib-dynload/spwd.so
%{_libdir}/python%{python_version}/lib-dynload/strop.so
%{_libdir}/python%{python_version}/lib-dynload/syslog.so
%{_libdir}/python%{python_version}/lib-dynload/termios.so
%{_libdir}/python%{python_version}/lib-dynload/time.so
%{_libdir}/python%{python_version}/lib-dynload/unicodedata.so
%{_libdir}/python%{python_version}/lib-dynload/zlib.so
%{_libdir}/python%{python_version}/lib-dynload/_codecs*.so
%{_libdir}/python%{python_version}/lib-dynload/_multibytecodec.so
%{_libdir}/python%{python_version}/lib-dynload/Python-%{tarversion}-py%{python_version}.egg-info
# these modules don't support 64-bit arches (disabled by setup.py)
%ifnarch alpha ia64 x86_64 s390x ppc64 ppc64le sparc64 aarch64 riscv64
# requires sizeof(int) == sizeof(long) == sizeof(char*)
%{_libdir}/python%{python_version}/lib-dynload/dl.so
%endif
%attr(755, root, root) %{_bindir}/pydoc
%attr(755, root, root) %{_bindir}/python
%attr(755, root, root) %{_bindir}/python%{python_version}
%attr(755, root, root) %{_bindir}/smtpd.py
%{_bindir}/python2
%exclude %{_bindir}/2to3
%changelog

395
python-bsddb6.patch Normal file
View File

@ -0,0 +1,395 @@
From: Jan Engelhardt <jengelh@inai.de>
Date: 2013-07-06 16:07:31.146616589 +0200
This patch was partially autogenerated:
- copying python-bsddb3-6.0.0 sources into the python-2.7.5 tree
- creating a diff -w against the unmodified python-2.7.5
- stripped all hunks that pertained to module renaming
- manually added db6 searching to setup.py
---
Lib/bsddb/test/test_all.py | 15 +++--
Lib/bsddb/test/test_misc.py | 5 +
Lib/bsddb/test/test_replication.py | 25 +-------
Modules/_bsddb.c | 108 +++++++++++++++++++++++++++++++++----
Modules/bsddb.h | 2
setup.py | 4 +
6 files changed, 118 insertions(+), 41 deletions(-)
--- a/Lib/bsddb/test/test_all.py
+++ b/Lib/bsddb/test/test_all.py
@@ -74,8 +74,9 @@ if sys.version_info[0] >= 3 :
key = key.decode(charset)
return (key, value.decode(charset))
- def __next__(self) :
- v = getattr(self._dbcursor, "next")()
+ def __next__(self, flags=0, dlen=-1, doff=-1) :
+ v = getattr(self._dbcursor, "next")(flags=flags, dlen=dlen,
+ doff=doff)
return self._fix(v)
next = __next__
@@ -128,8 +129,8 @@ if sys.version_info[0] >= 3 :
v = self._dbcursor.current(flags=flags, dlen=dlen, doff=doff)
return self._fix(v)
- def first(self) :
- v = self._dbcursor.first()
+ def first(self, flags=0, dlen=-1, doff=-1) :
+ v = self._dbcursor.first(flags=flags, dlen=dlen, doff=doff)
return self._fix(v)
def pget(self, key=None, data=None, flags=0) :
@@ -489,7 +490,11 @@ def print_versions():
print 'py module: %s' % getattr(bsddb, "__file"+suffix)
print 'extension module: %s' % getattr(bsddb, "__file"+suffix)
- print 'python version: %s' % sys.version
+ print 'Test working dir: %s' % get_test_path_prefix()
+ import platform
+ print 'python version: %s %s' % \
+ (sys.version.replace("\r", "").replace("\n", ""), \
+ platform.architecture()[0])
print 'My pid: %s' % os.getpid()
print '-=' * 38
--- a/Lib/bsddb/test/test_misc.py
+++ b/Lib/bsddb/test/test_misc.py
@@ -46,8 +46,9 @@ class MiscTestCase(unittest.TestCase):
d[repr(i)] = repr(100*i)
db.close()
db = hashopen(self.filename)
- rp = repr(db)
- self.assertEqual(rp, repr(d))
+ rp = repr(sorted(db.items()))
+ rd = repr(sorted(d.items()))
+ self.assertEqual(rp, rd)
db.close()
# http://sourceforge.net/tracker/index.php?func=detail&aid=1708868&group_id=13900&atid=313900
--- a/Lib/bsddb/test/test_replication.py
+++ b/Lib/bsddb/test/test_replication.py
@@ -165,21 +165,10 @@ class DBReplicationManager(DBReplication
# is not generated if the master has no new transactions.
# This is solved in BDB 4.6 (#15542).
import time
- timeout = time.time()+60
+ timeout = time.time()+10
while (time.time()<timeout) and not (self.confirmed_master and self.client_startupdone) :
time.sleep(0.02)
- # self.client_startupdone does not always get set to True within
- # the timeout. On windows this may be a deep issue, on other
- # platforms it is likely just a timing issue, especially on slow
- # virthost buildbots (see issue 3892 for more). Even though
- # the timeout triggers, the rest of this test method usually passes
- # (but not all of it always, see below). So we just note the
- # timeout on stderr and keep soldering on.
- if time.time()>timeout:
- import sys
- print >> sys.stderr, ("XXX: timeout happened before"
- "startup was confirmed - see issue 3892")
- startup_timeout = True
+ self.assertTrue(time.time()<timeout)
d = self.dbenvMaster.repmgr_site_list()
self.assertEqual(len(d), 1)
@@ -237,14 +226,6 @@ class DBReplicationManager(DBReplication
txn.commit()
if v is None :
time.sleep(0.02)
- # If startup did not happen before the timeout above, then this test
- # sometimes fails. This happens randomly, which causes buildbot
- # instability, but all the other bsddb tests pass. Since bsddb3 in the
- # stdlib is currently not getting active maintenance, and is gone in
- # py3k, we just skip the end of the test in that case.
- if time.time()>=timeout and startup_timeout:
- self.skipTest("replication test skipped due to random failure, "
- "see issue 3892")
self.assertLess(time.time(), timeout)
self.assertEqual("123", v)
@@ -375,7 +356,7 @@ class DBBaseReplication(DBReplication) :
# is not generated if the master has no new transactions.
# This is solved in BDB 4.6 (#15542).
import time
- timeout = time.time()+60
+ timeout = time.time()+10
while (time.time()<timeout) and not (self.confirmed_master and
self.client_startupdone) :
time.sleep(0.02)
--- a/Modules/_bsddb.c
+++ b/Modules/_bsddb.c
@@ -124,10 +124,14 @@ typedef int Py_ssize_t;
#define NUMBER_Check PyLong_Check
#define NUMBER_AsLong PyLong_AsLong
#define NUMBER_FromLong PyLong_FromLong
+#define NUMBER_FromUnsignedLong PyLong_FromUnsignedLong
#else
#define NUMBER_Check PyInt_Check
#define NUMBER_AsLong PyInt_AsLong
#define NUMBER_FromLong PyInt_FromLong
+#if (PY_VERSION_HEX >= 0x02050000)
+#define NUMBER_FromUnsignedLong PyInt_FromSize_t
+#endif
#endif
#ifdef WITH_THREAD
@@ -853,6 +857,18 @@ static void _addIntToDict(PyObject* dict
Py_XDECREF(v);
}
+#if (DBVER >= 60) && (PY_VERSION_HEX >= 0x02050000)
+/* add an unsigned integer to a dictionary using the given name as a key */
+static void _addUnsignedIntToDict(PyObject* dict, char *name, unsigned int value)
+{
+ PyObject* v = NUMBER_FromUnsignedLong((unsigned long) value);
+ if (!v || PyDict_SetItemString(dict, name, v))
+ PyErr_Clear();
+
+ Py_XDECREF(v);
+}
+#endif
+
/* The same, when the value is a time_t */
static void _addTimeTToDict(PyObject* dict, char *name, time_t value)
{
@@ -2677,13 +2693,21 @@ _default_cmp(const DBT *leftKey,
static int
_db_compareCallback(DB* db,
const DBT *leftKey,
- const DBT *rightKey)
+ const DBT *rightKey
+#if (DBVER >= 60)
+ , size_t *locp
+#endif
+ )
{
int res = 0;
PyObject *args;
PyObject *result = NULL;
DBObject *self = (DBObject *)db->app_private;
+# if (DBVER >= 60)
+ locp = NULL; /* As required by documentation */
+#endif
+
if (self == NULL || self->btCompareCallback == NULL) {
MYDB_BEGIN_BLOCK_THREADS;
PyErr_SetString(PyExc_TypeError,
@@ -2791,13 +2815,21 @@ DB_set_bt_compare(DBObject* self, PyObje
static int
_db_dupCompareCallback(DB* db,
const DBT *leftKey,
- const DBT *rightKey)
+ const DBT *rightKey
+#if (DBVER >= 60)
+ , size_t *locp
+#endif
+ )
{
int res = 0;
PyObject *args;
PyObject *result = NULL;
DBObject *self = (DBObject *)db->app_private;
+#if (DBVER >= 60)
+ locp = NULL; /* As required by documentation */
+#endif
+
if (self == NULL || self->dupCompareCallback == NULL) {
MYDB_BEGIN_BLOCK_THREADS;
PyErr_SetString(PyExc_TypeError,
@@ -3576,13 +3608,14 @@ Py_ssize_t DB_length(PyObject* _self)
err = self->db->stat(self->db, /*txnid*/ NULL, &sp, 0);
MYDB_END_ALLOW_THREADS;
+ if (makeDBError(err)) {
+ return -1;
+ }
+
/* All the stat structures have matching fields upto the ndata field,
so we can use any of them for the type cast */
size = ((DB_BTREE_STAT*)sp)->bt_ndata;
- if (err)
- return -1;
-
free(sp);
return size;
}
@@ -8420,12 +8453,22 @@ static PyObject*
DBSequence_get(DBSequenceObject* self, PyObject* args, PyObject* kwargs)
{
int err, flags = 0;
+#if (DBVER >= 60)
+ unsigned
+#endif
int delta = 1;
db_seq_t value;
PyObject *txnobj = NULL;
DB_TXN *txn = NULL;
static char* kwnames[] = {"delta", "txn", "flags", NULL };
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iOi:get", kwnames, &delta, &txnobj, &flags))
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+#if (DBVER >=60)
+ "|IOi:get",
+#else
+ "|iOi:get",
+#endif
+ kwnames, &delta, &txnobj, &flags))
return NULL;
CHECK_SEQUENCE_NOT_CLOSED(self)
@@ -8555,8 +8598,19 @@ DBSequence_remove(DBSequenceObject* self
static PyObject*
DBSequence_set_cachesize(DBSequenceObject* self, PyObject* args)
{
- int err, size;
- if (!PyArg_ParseTuple(args,"i:set_cachesize", &size))
+ int err;
+#if (DBVER >= 60)
+ unsigned
+#endif
+ int size;
+
+ if (!PyArg_ParseTuple(args,
+#if (DBVER >= 60)
+ "I:set_cachesize",
+#else
+ "i:set_cachesize",
+#endif
+ &size))
return NULL;
CHECK_SEQUENCE_NOT_CLOSED(self)
@@ -8571,7 +8625,11 @@ DBSequence_set_cachesize(DBSequenceObjec
static PyObject*
DBSequence_get_cachesize(DBSequenceObject* self)
{
- int err, size;
+ int err;
+#if (DBVER >= 60)
+ unsigned
+#endif
+ int size;
CHECK_SEQUENCE_NOT_CLOSED(self)
@@ -8700,6 +8758,9 @@ DBSequence_stat(DBSequenceObject* self,
#define MAKE_INT_ENTRY(name) _addIntToDict(dict_stat, #name, sp->st_##name)
+#if (DBVER >= 60) && (PY_VERSION_HEX >= 0x02050000)
+#define MAKE_UNSIGNED_INT_ENTRY(name) _addUnsignedIntToDict(dict_stat, #name, sp->st_##name)
+#endif
#define MAKE_LONG_LONG_ENTRY(name) _addDb_seq_tToDict(dict_stat, #name, sp->st_##name)
MAKE_INT_ENTRY(wait);
@@ -8709,10 +8770,15 @@ DBSequence_stat(DBSequenceObject* self,
MAKE_LONG_LONG_ENTRY(last_value);
MAKE_LONG_LONG_ENTRY(min);
MAKE_LONG_LONG_ENTRY(max);
+#if (DBVER >= 60) && (PY_VERSION_HEX >= 0x02050000)
+ MAKE_UNSIGNED_INT_ENTRY(cache_size);
+#else
MAKE_INT_ENTRY(cache_size);
+#endif
MAKE_INT_ENTRY(flags);
#undef MAKE_INT_ENTRY
+#undef MAKE_UNSIGNED_INT_ENTRY
#undef MAKE_LONG_LONG_ENTRY
free(sp);
@@ -9014,7 +9080,7 @@ static PyMethodDef DBEnv_methods[] = {
{"txn_recover", (PyCFunction)DBEnv_txn_recover, METH_NOARGS},
#if (DBVER < 48)
{"set_rpc_server", (PyCFunction)DBEnv_set_rpc_server,
- METH_VARARGS|METH_KEYWORDS},
+ METH_VARARGS||METH_KEYWORDS},
#endif
{"set_mp_max_openfd", (PyCFunction)DBEnv_set_mp_max_openfd, METH_VARARGS},
{"get_mp_max_openfd", (PyCFunction)DBEnv_get_mp_max_openfd, METH_NOARGS},
@@ -9986,6 +10052,10 @@ PyMODINIT_FUNC PyInit__bsddb(void) /
ADD_INT(d, DB_LOG_ZERO);
#endif
+#if (DBVER >= 60)
+ ADD_INT(d, DB_LOG_BLOB);
+#endif
+
#if (DBVER >= 44)
ADD_INT(d, DB_DSYNC_DB);
#endif
@@ -10046,6 +10116,10 @@ PyMODINIT_FUNC PyInit__bsddb(void) /
ADD_INT(d, DB_EVENT_REG_PANIC);
#endif
+#if (DBVER >= 60)
+ ADD_INT(d, DB_EVENT_REP_AUTOTAKEOVER_FAILED);
+#endif
+
#if (DBVER >=52)
ADD_INT(d, DB_EVENT_REP_SITE_ADDED);
ADD_INT(d, DB_EVENT_REP_SITE_REMOVED);
@@ -10150,6 +10224,20 @@ PyMODINIT_FUNC PyInit__bsddb(void) /
ADD_INT(d, DB_REP_CONF_INMEM);
#endif
+#if (DBVER >= 60)
+ ADD_INT(d, DB_REPMGR_ISVIEW);
+#endif
+
+#if (DBVER >= 60)
+ ADD_INT(d, DB_DBT_BLOB);
+#endif
+
+#if (DBVER >= 60)
+ ADD_INT(d, DB_STREAM_READ);
+ ADD_INT(d, DB_STREAM_WRITE);
+ ADD_INT(d, DB_STREAM_SYNC_WRITE);
+#endif
+
ADD_INT(d, DB_TIMEOUT);
#if (DBVER >= 50)
--- a/Modules/bsddb.h
+++ b/Modules/bsddb.h
@@ -110,7 +110,7 @@
#error "eek! DBVER can't handle minor versions > 9"
#endif
-#define PY_BSDDB_VERSION "5.3.0"
+#define PY_BSDDB_VERSION "6.0.0"
/* Python object definitions */
--- a/setup.py
+++ b/setup.py
@@ -951,7 +951,7 @@ class PyBuildExt(build_ext):
# a release. Most open source OSes come with one or more
# versions of BerkeleyDB already installed.
- max_db_ver = (5, 3)
+ max_db_ver = (6, 0)
min_db_ver = (4, 3)
db_setup_debug = False # verbose debug prints from this script?
@@ -991,6 +991,7 @@ class PyBuildExt(build_ext):
# construct a list of paths to look for the header file in on
# top of the normal inc_dirs.
db_inc_paths = [
+ '/usr/include/db6',
'/usr/include/db4',
'/usr/local/include/db4',
'/opt/sfw/include/db4',
@@ -1030,6 +1031,7 @@ class PyBuildExt(build_ext):
for dn in inc_dirs:
std_variants.append(os.path.join(dn, 'db3'))
std_variants.append(os.path.join(dn, 'db4'))
+ std_variants.append(os.path.join(dn, 'db6'))
for x in gen_db_minor_ver_nums(4):
std_variants.append(os.path.join(dn, "db4%d"%x))
std_variants.append(os.path.join(dn, "db4.%d"%x))

23
python-bundle-lang.patch Normal file
View File

@ -0,0 +1,23 @@
Index: Python-2.7.13/Lib/gettext.py
===================================================================
--- Python-2.7.13.orig/Lib/gettext.py
+++ Python-2.7.13/Lib/gettext.py
@@ -58,6 +58,7 @@ __all__ = ['NullTranslations', 'GNUTrans
]
_default_localedir = os.path.join(sys.prefix, 'share', 'locale')
+_default_bundlelocaledir = os.path.join(sys.prefix, 'share', 'locale-bundle')
# Expression parsing for plural form selection.
#
@@ -496,6 +497,10 @@ class GNUTranslations(NullTranslations):
# Locate a .mo file using the gettext strategy
def find(domain, localedir=None, languages=None, all=0):
+ if localedir in [None, _default_localedir]:
+ bundle = find(domain, localedir=_default_bundlelocaledir, languages=languages, all=all)
+ if bundle:
+ return bundle
# Get some reasonable defaults for arguments that were not supplied
if localedir is None:
localedir = _default_localedir

View File

@ -0,0 +1,82 @@
--- a/Lib/distutils/command/install.py
+++ b/Lib/distutils/command/install.py
@@ -154,6 +154,8 @@
('record=', None,
"filename in which to record list of installed files"),
+ ('record-rpm=', None,
+ "filename in which to record list of installed files and directories suitable as filelist for rpm"),
]
boolean_options = ['compile', 'force', 'skip-build', 'user']
@@ -229,6 +231,7 @@
#self.install_info = None
self.record = None
+ self.record_rpm = None
# -- Option finalizing methods -------------------------------------
@@ -578,12 +581,61 @@
self.create_path_file()
# write list of installed files, if requested.
- if self.record:
+ if self.record or self.record_rpm:
outputs = self.get_outputs()
if self.root: # strip any package prefix
root_len = len(self.root)
for counter in xrange(len(outputs)):
outputs[counter] = outputs[counter][root_len:]
+ if self.record_rpm: # add directories
+ self.record = self.record_rpm
+ dirs = []
+ # directories to reject:
+ rejectdirs = [
+ '/etc',
+ '/',
+ '',
+ self.prefix,
+ self.exec_prefix,
+ self.install_base,
+ self.install_platbase,
+ self.install_purelib,
+ self.install_platlib,
+ self.install_headers[:len(self.install_headers) - len(self.distribution.get_name()) - 1],
+ self.install_libbase,
+ self.install_scripts,
+ self.install_data,
+ os.path.join(self.install_data, 'share'),
+ os.path.join(self.install_data, 'share', 'doc'),
+ ]
+ # directories whose childs reject:
+ rejectdirs2 = [
+ os.path.join(self.install_data, 'share', 'man'),
+ ]
+ # directories whose grandsons reject:
+ rejectdirs3 = [
+ os.path.join(self.install_data, 'share', 'man'),
+ os.path.join(self.install_data, 'share', 'locale'),
+ ]
+ for counter in xrange(len(rejectdirs)):
+ if len(rejectdirs[counter]) > root_len:
+ rejectdirs[counter] = rejectdirs[counter][root_len:]
+ for counter in xrange(len(rejectdirs2)):
+ if len(rejectdirs2[counter]) > root_len:
+ rejectdirs2[counter] = rejectdirs2[counter][root_len:]
+ for counter in xrange(len(rejectdirs3)):
+ if len(rejectdirs3[counter]) > root_len:
+ rejectdirs3[counter] = rejectdirs3[counter][root_len:]
+ for counter in xrange(len(outputs)):
+ directory = os.path.dirname(outputs[counter])
+ while directory not in rejectdirs and \
+ os.path.dirname(directory) not in rejectdirs2 and \
+ os.path.dirname(os.path.dirname(directory)) not in rejectdirs3:
+ dirname = '%dir ' + directory
+ if dirname not in dirs:
+ dirs.append(dirname)
+ directory = os.path.dirname(directory)
+ outputs += dirs
self.execute(write_file,
(self.record, outputs),
"writing list of installed files to '%s'" %

2720
python-doc.changes Normal file

File diff suppressed because it is too large Load Diff

304
python-doc.spec Normal file
View File

@ -0,0 +1,304 @@
#
# spec file for package python-doc
#
# Copyright (c) 2024 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
Name: python-doc
Version: 2.7.18
Release: 0
Summary: Additional Package Documentation for Python
License: Python-2.0
Group: Development/Languages/Python
URL: https://www.python.org/
%define tarname Python-%{version}
Source0: %{tarname}.tar.xz
# docs for current version are regenerated every day
# this messes with autobuild "file changed" checks
#Source2: https://docs.python.org/%%{version}/archives/python-%%{pyver}-docs-pdf-a4.tar.bz2
#Source3: https://docs.python.org/%%{version}/archives/python-%%{pyver}-docs-pdf-letter.tar.bz2
Source2: python-%{version}-docs-pdf-a4.tar.bz2
Source3: python-%{version}-docs-pdf-letter.tar.bz2
# For Patch 66
Source66: recursion.tar
%if 0%{?suse_version} >= 1500
BuildRequires: python3-Sphinx
%else
BuildRequires: python-Sphinx
%endif
BuildRequires: xz
# COMMON-PATCH-BEGIN
Patch1: python-2.7-dirs.patch
Patch2: python-distutils-rpm-8.patch
Patch3: python-2.7.5-multilib.patch
Patch4: python-2.5.1-sqlite.patch
Patch5: python-2.7.4-canonicalize2.patch
Patch7: python-2.6-gettext-plurals.patch
Patch8: python-2.6b3-curses-panel.patch
Patch13: python-2.7.2-fix_date_time_compiler.patch
Patch17: remove-static-libpython.patch
# PATCH-FEATURE-OPENSUSE python-bundle-lang.patch bnc#617751 dimstar@opensuse.org -- gettext: when looking in default_localedir also check in locale-bundle.
Patch20: python-bundle-lang.patch
Patch24: python-bsddb6.patch
# PATCH-FIX-UPSTREAM accept directory-based CA paths as well
Patch33: python-2.7.9-ssl_ca_path.patch
# PATCH-FEATURE-SLE disable SSL verification-by-default in http clients
Patch34: python-2.7.9-sles-disable-verification-by-default.patch
# PATCH-FIX-UPSTREAM do not use non-ASCII filename in test_ssl.py
Patch35: do-not-use-non-ascii-in-test_ssl.patch
# PATCH-FIX-UPSTREAM bmwiedemann@suse.de -- allow python packages to build reproducibly
Patch38: reproducible.patch
# bypass boo#1078485 random failing tests
Patch40: python-skip_random_failing_tests.patch
# PATCH-FIX-UPSTREAM sorted tar https://github.com/python/cpython/pull/2263
Patch41: python-sorted_tar.patch
# https://github.com/python/cpython/pull/9624 (https://bugs.python.org/issue34834)
Patch47: openssl-111-middlebox-compat.patch
# PATCH-FIX-SUSE python default SSLContext doesn't contain OP_CIPHER_SERVER_PREFERENCE
Patch48: openssl-111-ssl_options.patch
# PATCH-FIX-UPSTREAM CVE-2019-5010-null-defer-x509-cert-DOS.patch bnc#1122191 mcepl@suse.com
# gh#python/cpython#11569
# Fix segfault in ssl's cert parser
Patch49: CVE-2019-5010-null-defer-x509-cert-DOS.patch
# PATCH-FIX-UPSTREAM bpo36160-init-sysconfig_vars.patch gh#python/cpython#12131 mcepl@suse.com
# Initialize sysconfig variables in test_site.
Patch50: bpo36160-init-sysconfig_vars.patch
# PATCH-FIX-UPSTREAM CVE-2017-18207.patch gh#python/cpython#4437 psimons@suse.com
# Add check for channels of wav file in Lib/wave.py
Patch51: CVE-2017-18207.patch
# PATCH-FIX-UPSTREAM gh#python/cpython#12341
Patch55: bpo36302-sort-module-sources.patch
# Fix installation in /usr/local (boo#1071941), adapted from Fedora
# https://src.fedoraproject.org/rpms/python3/blob/master/f/00251-change-user-install-location.patch
# Set values of prefix and exec_prefix in distutils install command
# to /usr/local if executable is /usr/bin/python* and RPM build
# is not detected to make pip and distutils install into separate location
Patch56: adapted-from-F00251-change-user-install-location.patch
# Switch couple of tests failing on acient SLE-12
Patch57: python-2.7.17-switch-off-failing-SSL-tests.patch
# PATCH-FIX-UPSTREAM CVE-2020-8492-urllib-ReDoS.patch bsc#1162367 mcepl@suse.com
# Fixes Python urrlib allowed an HTTP server to conduct Regular
# Expression Denial of Service (ReDoS)
Patch58: CVE-2020-8492-urllib-ReDoS.patch
# PATCH-FIX-UPSTREAM CVE-2019-9674-zip-bomb.patch bsc#1162825 mcepl@suse.com
# Improve documentation warning against the possible zip bombs
Patch59: CVE-2019-9674-zip-bomb.patch
# PATCH-FIX-UPSTREAM configure_PYTHON_FOR_REGEN.patch bsc#1078326 mcepl@suse.com
# PYTHON_FOR_REGEN value is set very weird upstream
Patch60: configure_PYTHON_FOR_REGEN.patch
# PATCH-FIX-SLE CVE-2021-3177-buf_ovrfl_PyCArg_repr.patch bsc#1181126 mcepl@suse.com
# buffer overflow in PyCArg_repr in _ctypes/callproc.c, which may lead to remote code execution
Patch61: CVE-2021-3177-buf_ovrfl_PyCArg_repr.patch
# PATCH-FIX-UPSTREAM CVE-2021-23336-only-amp-as-query-sep.patch bsc#[0-9]+ mcepl@suse.com
# this patch makes things totally awesome
Patch62: CVE-2021-23336-only-amp-as-query-sep.patch
# PATCH-FIX-UPSTREAM CVE-2021-3737-fix-HTTP-client-infinite-line-reading-after-a-HTTP-100-Continue.patch boo#1189241 gh#python/cpython#25916
Patch63: CVE-2021-3737-fix-HTTP-client-infinite-line-reading-after-a-HTTP-100-Continue.patch
# PATCH-FIX-UPSTREAM CVE-2021-3733-fix-ReDoS-in-request.patch boo#1189287 gh#python/cpython#24391
Patch64: CVE-2021-3733-fix-ReDoS-in-request.patch
# PATCH-FIX-UPSTREAM sphinx-update-removed-function.patch bpo#35293 gh#python/cpython#22198 -- fix doc build
Patch65: sphinx-update-removed-function.patch
# PATCH-FIX-UPSTREAM CVE-2019-20907_tarfile-inf-loop.patch bsc#1174091 mcepl@suse.com
# avoid possible infinite loop in specifically crafted tarball (CVE-2019-20907)
# REQUIRES SOURCE 66
Patch66: CVE-2019-20907_tarfile-inf-loop.patch
# PATCH-FIX-UPSTREAM CVE-2020-26116-httplib-header-injection.patch bsc#1177211
# Fixes httplib to disallow control characters in method to avoid header
# injection
Patch67: CVE-2020-26116-httplib-header-injection.patch
# PATCH-FIX-UPSTREAM CVE-2021-4189-ftplib-trust-PASV-resp.patch bsc#1194146 mcepl@suse.com
# Make ftplib not trust the PASV response. (gh#python/cpython#24838)
Patch68: CVE-2021-4189-ftplib-trust-PASV-resp.patch
# PATCH-FIX-UPSTREAM CVE-2022-0391-urllib_parse-newline-parsing.patch bsc#1195396 mcepl@suse.com
# whole long discussion is on bpo#43882
# fix for santization URLs containing ASCII newline and tabs in urllib.parse
Patch69: CVE-2022-0391-urllib_parse-newline-parsing.patch
# PATCH-FIX-UPSTREAM CVE-2015-20107-mailcap-unsafe-filenames.patch bsc#1198511 mcepl@suse.com
# avoid the command injection in the mailcap module.
Patch70: CVE-2015-20107-mailcap-unsafe-filenames.patch
# PATCH-FIX-UPSTREAM CVE-2021-28861 bsc#1202624
# Coerce // to / in Lib/BaseHTTPServer.py
Patch71: CVE-2021-28861-double-slash-path.patch
Patch72: bpo34990-2038-problem-compileall.patch
# PATCH-FIX-UPSTREAM CVE-2022-45061-DoS-by-IDNA-decode.patch bsc#1205244 mcepl@suse.com
# Avoid DoS by decoding IDNA for too long domain names
Patch73: CVE-2022-45061-DoS-by-IDNA-decode.patch
# PATCH-FIX-UPSTREAM skip_unverified_test.patch mcepl@suse.com
# switching verification off on the old SLE doesn't work
Patch74: skip_unverified_test.patch
# PATCH-FIX-UPSTREAM CVE-2023-24329-blank-URL-bypass.patch bsc#1208471 mcepl@suse.com
# blocklist bypass via the urllib.parse component when supplying
# a URL that starts with blank characters
Patch75: CVE-2023-24329-blank-URL-bypass.patch
# PATCH-FIX-OPENSUSE PygmentsBridge-trime_doctest_flags.patch mcepl@suse.com
# Build documentation even without PygmentsBridge.trim_doctest_flags
Patch76: PygmentsBridge-trime_doctest_flags.patch
# PATCH-FIX-UPSTREAM CVE-2023-27043-email-parsing-errors.patch bsc#1210638 mcepl@suse.com
# Detect email address parsing errors and return empty tuple to
# indicate the parsing error (old API), modified for fixing bsc#1222537,
# so that email.utils.parseaddr accepts unicode string
Patch77: CVE-2023-27043-email-parsing-errors.patch
# PATCH-FIX-UPSTREAM CVE-2022-48565-plistlib-XML-vulns.patch bsc#1214685 mcepl@suse.com
# Reject entity declarations in plists
Patch78: CVE-2022-48565-plistlib-XML-vulns.patch
# PATCH-FIX-UPSTREAM CVE-2023-40217-avoid-ssl-pre-close.patch gh#python/cpython#108315
Patch79: CVE-2023-40217-avoid-ssl-pre-close.patch
# PATCH-FIX-UPSTREAM CVE-2022-48566-compare_digest-more-constant.patch bsc#1214691 mcepl@suse.com
# Make compare_digest more constant-time
Patch80: CVE-2022-48566-compare_digest-more-constant.patch
# PATCH-FIX-OPENSUSE CVE-2023-52425-libexpat-2.6.0-remove-failing-tests.patch bpo#3151 mcepl@suse.com
# We don't have fix for bpo#3151 and it is just not supported
Patch81: CVE-2023-52425-libexpat-2.6.0-remove-failing-tests.patch
# PATCH-FIX-UPSTREAM CVE-2024-0450-zipfile-avoid-quoted-overlap-zipbomb.patch bsc#1221854 mcepl@suse.com
# detecting the vulnerability of the "quoted-overlap" zipbomb (from gh#python/cpython!110016).
Patch82: CVE-2024-0450-zipfile-avoid-quoted-overlap-zipbomb.patch
# COMMON-PATCH-END
Provides: pyth_doc = %{version}
Provides: pyth_ps = %{version}
Obsoletes: pyth_doc < %{version}
Obsoletes: pyth_ps < %{version}
BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildArch: noarch
Enhances: python = %{version}
Provides: python2-doc = %{version}
%description
Tutorial, Global Module Index, Language Reference, Library Reference,
Extending and Embedding Reference, Python/C API Reference, Documenting
Python, and Macintosh Module Reference in HTML format.
%package pdf
Summary: Python PDF Documentation
Group: Development/Languages/Python
Provides: pyth_pdf = %{version}
Obsoletes: pyth_pdf < %{version}
Provides: python2-doc-pdf = %{version}
%description pdf
Tutorial, Global Module Index, Language Reference, Library Reference,
Extending and Embedding Reference, Python/C API Reference, Documenting
Python, and Macintosh Module Reference in PDF format.
%prep
%setup -q -n %{tarname}
# COMMON-PREP-BEGIN
%patch -P 1 -p1
%patch -P 2 -p1
%patch -P 3 -p1
%patch -P 4 -p1
%patch -P 5 -p1
%patch -P 7 -p1
%patch -P 8 -p1
%patch -P 13 -p1
%patch -P 17 -p1
%patch -P 20 -p1
%patch -P 24 -p1
%patch -P 33 -p1
%if %{suse_version} < 1500 && !0%{?is_opensuse}
%patch -P 34 -p1
%endif
%patch -P 35 -p1
%patch -P 38 -p1
%ifarch ppc ppc64 ppc64le
%patch -P 40 -p1
%endif
%patch -P 41 -p1
%if %{suse_version} >= 1500 || (0%{?sle_version} && 0%{?sle_version} >= 120400)
%patch -P 47 -p1
%patch -P 48 -p1
%endif
# SLE-12 needs to skip more
%if %{suse_version} == 1315
%patch -P 57 -p1
%endif
%patch -P 49 -p1
%patch -P 50 -p1
%patch -P 51 -p1
%patch -P 55 -p1
%patch -P 56 -p1
%patch -P 58 -p1
%patch -P 59 -p1
%patch -P 60 -p1
%patch -P 61 -p1
%patch -P 62 -p1
%patch -P 63 -p1
%patch -P 64 -p1
%patch -P 65 -p1
%patch -P 66 -p1
%patch -P 67 -p1
%patch -P 68 -p1
%patch -P 69 -p1
%patch -P 70 -p1
%patch -P 71 -p1
%patch -P 72 -p1
%patch -P 73 -p1
%if 0%{?sle_version} && 0%{?sle_version} < 150000
%patch -P 74 -p1
%endif
%patch -P 75 -p1
%patch -P 76 -p1
%patch -P 77 -p1
%patch -P 78 -p1
%patch -P 79 -p1
%patch -P 80 -p1
%patch -P 81 -p1
%patch -P 82 -p1
# For patch 66
cp -v %{SOURCE66} Lib/test/recursion.tar
# drop Autoconf version requirement
sed -i 's/^version_required/dnl version_required/' configure.ac
# COMMON-PREP-END
%build
TODAY_DATE=`date -r %{S:0} "+%B %d, %Y"`
# TODO use not date of tarball but date of latest patch
pushd Doc
sed -i "s/^today = .*/today = '$TODAY_DATE'/" conf.py
%if 0%{?suse_version} < 1320
# lower sphinx version requirement, docs seem to work fine with 1.1
sed -i "s/^needs_sphinx = .*/needs_sphinx = '1.1'/" conf.py
%endif
make html
popd
%install
export PDOCS=%{buildroot}%{_docdir}/python
install -d -m 755 $PDOCS/Misc
rm Doc/build/html/.buildinfo
mv Doc/build/html $PDOCS/html
tar xfj %{SOURCE2} -C $PDOCS
mv $PDOCS/docs-pdf $PDOCS/paper-a4
tar xfj %{SOURCE3} -C $PDOCS
mv $PDOCS/docs-pdf $PDOCS/paper-letter
# this is part of main package
#install -c -m 644 README $PDOCS/README
for i in Misc/* ; do
[ -f $i ] && install -c -m 644 $i $PDOCS/Misc/
done
%files
%dir %{_docdir}/python
%doc %{_docdir}/python/Misc
%doc %{_docdir}/python/html
#%%doc %%{_docdir}/python/README
%files pdf
%doc %{_docdir}/python/paper-a4
%doc %{_docdir}/python/paper-letter
%changelog

View File

@ -0,0 +1,50 @@
---
Lib/test/test_multiprocessing.py | 1 +
Lib/test/test_subprocess.py | 1 +
Lib/test/test_telnetlib.py | 2 ++
3 files changed, 4 insertions(+)
Index: Python-2.7.14/Lib/test/test_subprocess.py
===================================================================
--- Python-2.7.14.orig/Lib/test/test_subprocess.py
+++ Python-2.7.14/Lib/test/test_subprocess.py
@@ -654,6 +654,7 @@ class ProcessTestCase(BaseTestCase):
'ab ""')
+ @unittest.skip("transient failure on PowerPC")
def test_poll(self):
p = subprocess.Popen([sys.executable,
"-c", "import time; time.sleep(1)"])
Index: Python-2.7.14/Lib/test/test_telnetlib.py
===================================================================
--- Python-2.7.14.orig/Lib/test/test_telnetlib.py
+++ Python-2.7.14/Lib/test/test_telnetlib.py
@@ -134,6 +134,7 @@ class ReadTests(TestCase):
data = telnet.read_until('match')
self.assertEqual(data, ''.join(want[:-2]))
+ @unittest.skip("transient failure on PowerPC")
def test_read_until_B(self):
# test the timeout - it does NOT raise socket.timeout
want = ['hello', self.block_long, 'not seen', EOF_sigil]
@@ -420,6 +421,7 @@ class ExpectTests(TestCase):
(_,_,data) = telnet.expect(['match'])
self.assertEqual(data, ''.join(want[:-2]))
+ @unittest.skip("transient failure on PowerPC")
def test_expect_B(self):
# test the timeout - it does NOT raise socket.timeout
want = ['hello', self.block_long, 'not seen', EOF_sigil]
Index: Python-2.7.14/Lib/test/test_multiprocessing.py
===================================================================
--- Python-2.7.14.orig/Lib/test/test_multiprocessing.py
+++ Python-2.7.14/Lib/test/test_multiprocessing.py
@@ -1219,6 +1219,7 @@ class _TestPool(BaseTestCase):
self.assertEqual(get(), 49)
self.assertTimingAlmostEqual(get.elapsed, TIMEOUT1)
+ @unittest.skip("transient failure on PowerPC")
def test_async_timeout(self):
res = self.pool.apply_async(sqr, (6, TIMEOUT2 + 1.0))
get = TimingWrapper(res.get)

21
python-sorted_tar.patch Normal file
View File

@ -0,0 +1,21 @@
commit 6936e36efcc0a75c7d5e67b949c9749d61fa5ead
Author: Bernhard M. Wiedemann <bwiedemann@suse.de>
Date: Sun Jun 18 03:50:25 2017 +0200
tarfile: sort directory listing
to generate tar files in a more reproducible way
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index adf91d5382..36f6ed7167 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -2027,7 +2027,7 @@ class TarFile(object):
elif tarinfo.isdir():
self.addfile(tarinfo)
if recursive:
- for f in os.listdir(name):
+ for f in sorted(os.listdir(name)):
self.add(os.path.join(name, f), os.path.join(arcname, f),
recursive, exclude, filter)

2720
python.changes Normal file

File diff suppressed because it is too large Load Diff

BIN
python.keyring Normal file

Binary file not shown.

633
python.spec Normal file
View File

@ -0,0 +1,633 @@
#
# spec file for package python
#
# Copyright (c) 2024 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
Name: python
Version: 2.7.18
Release: 0
Summary: Python Interpreter
License: Python-2.0
Group: Development/Languages/Python
URL: https://www.python.org/
%define tarversion %{version}
%define tarname Python-%{tarversion}
Source0: https://www.python.org/ftp/python/%{version}/%{tarname}.tar.xz
Source1: README.SUSE
Source8: sle_tls_checks_policy.py
#Source11: testfiles.tar.bz2
Source50: idle.appdata.xml
Source51: idle.desktop
# issues with copyrighted Unicode testing files
# For Patch 66
Source66: recursion.tar
# !!!!!!!!!!!!!!
# do not add or edit patches here. please edit python-base.spec
# instead and run pre_checkin.sh
# !!!!!!!!!!!!!!
# COMMON-PATCH-BEGIN
Patch1: python-2.7-dirs.patch
Patch2: python-distutils-rpm-8.patch
Patch3: python-2.7.5-multilib.patch
Patch4: python-2.5.1-sqlite.patch
Patch5: python-2.7.4-canonicalize2.patch
Patch7: python-2.6-gettext-plurals.patch
Patch8: python-2.6b3-curses-panel.patch
Patch13: python-2.7.2-fix_date_time_compiler.patch
Patch17: remove-static-libpython.patch
# PATCH-FEATURE-OPENSUSE python-bundle-lang.patch bnc#617751 dimstar@opensuse.org -- gettext: when looking in default_localedir also check in locale-bundle.
Patch20: python-bundle-lang.patch
Patch24: python-bsddb6.patch
# PATCH-FIX-UPSTREAM accept directory-based CA paths as well
Patch33: python-2.7.9-ssl_ca_path.patch
# PATCH-FEATURE-SLE disable SSL verification-by-default in http clients
Patch34: python-2.7.9-sles-disable-verification-by-default.patch
# PATCH-FIX-UPSTREAM do not use non-ASCII filename in test_ssl.py
Patch35: do-not-use-non-ascii-in-test_ssl.patch
# PATCH-FIX-UPSTREAM bmwiedemann@suse.de -- allow python packages to build reproducibly
Patch38: reproducible.patch
# bypass boo#1078485 random failing tests
Patch40: python-skip_random_failing_tests.patch
# PATCH-FIX-UPSTREAM sorted tar https://github.com/python/cpython/pull/2263
Patch41: python-sorted_tar.patch
# https://github.com/python/cpython/pull/9624 (https://bugs.python.org/issue34834)
Patch47: openssl-111-middlebox-compat.patch
# PATCH-FIX-SUSE python default SSLContext doesn't contain OP_CIPHER_SERVER_PREFERENCE
Patch48: openssl-111-ssl_options.patch
# PATCH-FIX-UPSTREAM CVE-2019-5010-null-defer-x509-cert-DOS.patch bnc#1122191 mcepl@suse.com
# gh#python/cpython#11569
# Fix segfault in ssl's cert parser
Patch49: CVE-2019-5010-null-defer-x509-cert-DOS.patch
# PATCH-FIX-UPSTREAM bpo36160-init-sysconfig_vars.patch gh#python/cpython#12131 mcepl@suse.com
# Initialize sysconfig variables in test_site.
Patch50: bpo36160-init-sysconfig_vars.patch
# PATCH-FIX-UPSTREAM CVE-2017-18207.patch gh#python/cpython#4437 psimons@suse.com
# Add check for channels of wav file in Lib/wave.py
Patch51: CVE-2017-18207.patch
# PATCH-FIX-UPSTREAM gh#python/cpython#12341
Patch55: bpo36302-sort-module-sources.patch
# Fix installation in /usr/local (boo#1071941), adapted from Fedora
# https://src.fedoraproject.org/rpms/python3/blob/master/f/00251-change-user-install-location.patch
# Set values of prefix and exec_prefix in distutils install command
# to /usr/local if executable is /usr/bin/python* and RPM build
# is not detected to make pip and distutils install into separate location
Patch56: adapted-from-F00251-change-user-install-location.patch
# Switch couple of tests failing on acient SLE-12
Patch57: python-2.7.17-switch-off-failing-SSL-tests.patch
# PATCH-FIX-UPSTREAM CVE-2020-8492-urllib-ReDoS.patch bsc#1162367 mcepl@suse.com
# Fixes Python urrlib allowed an HTTP server to conduct Regular
# Expression Denial of Service (ReDoS)
Patch58: CVE-2020-8492-urllib-ReDoS.patch
# PATCH-FIX-UPSTREAM CVE-2019-9674-zip-bomb.patch bsc#1162825 mcepl@suse.com
# Improve documentation warning against the possible zip bombs
Patch59: CVE-2019-9674-zip-bomb.patch
# PATCH-FIX-UPSTREAM configure_PYTHON_FOR_REGEN.patch bsc#1078326 mcepl@suse.com
# PYTHON_FOR_REGEN value is set very weird upstream
Patch60: configure_PYTHON_FOR_REGEN.patch
# PATCH-FIX-SLE CVE-2021-3177-buf_ovrfl_PyCArg_repr.patch bsc#1181126 mcepl@suse.com
# buffer overflow in PyCArg_repr in _ctypes/callproc.c, which may lead to remote code execution
Patch61: CVE-2021-3177-buf_ovrfl_PyCArg_repr.patch
# PATCH-FIX-UPSTREAM CVE-2021-23336-only-amp-as-query-sep.patch bsc#[0-9]+ mcepl@suse.com
# this patch makes things totally awesome
Patch62: CVE-2021-23336-only-amp-as-query-sep.patch
# PATCH-FIX-UPSTREAM CVE-2021-3737-fix-HTTP-client-infinite-line-reading-after-a-HTTP-100-Continue.patch boo#1189241 gh#python/cpython#25916
Patch63: CVE-2021-3737-fix-HTTP-client-infinite-line-reading-after-a-HTTP-100-Continue.patch
# PATCH-FIX-UPSTREAM CVE-2021-3733-fix-ReDoS-in-request.patch boo#1189287 gh#python/cpython#24391
Patch64: CVE-2021-3733-fix-ReDoS-in-request.patch
# PATCH-FIX-UPSTREAM sphinx-update-removed-function.patch bpo#35293 gh#python/cpython#22198 -- fix doc build
Patch65: sphinx-update-removed-function.patch
# PATCH-FIX-UPSTREAM CVE-2019-20907_tarfile-inf-loop.patch bsc#1174091 mcepl@suse.com
# avoid possible infinite loop in specifically crafted tarball (CVE-2019-20907)
# REQUIRES SOURCE 66
Patch66: CVE-2019-20907_tarfile-inf-loop.patch
# PATCH-FIX-UPSTREAM CVE-2020-26116-httplib-header-injection.patch bsc#1177211
# Fixes httplib to disallow control characters in method to avoid header
# injection
Patch67: CVE-2020-26116-httplib-header-injection.patch
# PATCH-FIX-UPSTREAM CVE-2021-4189-ftplib-trust-PASV-resp.patch bsc#1194146 mcepl@suse.com
# Make ftplib not trust the PASV response. (gh#python/cpython#24838)
Patch68: CVE-2021-4189-ftplib-trust-PASV-resp.patch
# PATCH-FIX-UPSTREAM CVE-2022-0391-urllib_parse-newline-parsing.patch bsc#1195396 mcepl@suse.com
# whole long discussion is on bpo#43882
# fix for santization URLs containing ASCII newline and tabs in urllib.parse
Patch69: CVE-2022-0391-urllib_parse-newline-parsing.patch
# PATCH-FIX-UPSTREAM CVE-2015-20107-mailcap-unsafe-filenames.patch bsc#1198511 mcepl@suse.com
# avoid the command injection in the mailcap module.
Patch70: CVE-2015-20107-mailcap-unsafe-filenames.patch
# PATCH-FIX-UPSTREAM CVE-2021-28861 bsc#1202624
# Coerce // to / in Lib/BaseHTTPServer.py
Patch71: CVE-2021-28861-double-slash-path.patch
Patch72: bpo34990-2038-problem-compileall.patch
# PATCH-FIX-UPSTREAM CVE-2022-45061-DoS-by-IDNA-decode.patch bsc#1205244 mcepl@suse.com
# Avoid DoS by decoding IDNA for too long domain names
Patch73: CVE-2022-45061-DoS-by-IDNA-decode.patch
# PATCH-FIX-UPSTREAM skip_unverified_test.patch mcepl@suse.com
# switching verification off on the old SLE doesn't work
Patch74: skip_unverified_test.patch
# PATCH-FIX-UPSTREAM CVE-2023-24329-blank-URL-bypass.patch bsc#1208471 mcepl@suse.com
# blocklist bypass via the urllib.parse component when supplying
# a URL that starts with blank characters
Patch75: CVE-2023-24329-blank-URL-bypass.patch
# PATCH-FIX-OPENSUSE PygmentsBridge-trime_doctest_flags.patch mcepl@suse.com
# Build documentation even without PygmentsBridge.trim_doctest_flags
Patch76: PygmentsBridge-trime_doctest_flags.patch
# PATCH-FIX-UPSTREAM CVE-2023-27043-email-parsing-errors.patch bsc#1210638 mcepl@suse.com
# Detect email address parsing errors and return empty tuple to
# indicate the parsing error (old API), modified for fixing bsc#1222537,
# so that email.utils.parseaddr accepts unicode string
Patch77: CVE-2023-27043-email-parsing-errors.patch
# PATCH-FIX-UPSTREAM CVE-2022-48565-plistlib-XML-vulns.patch bsc#1214685 mcepl@suse.com
# Reject entity declarations in plists
Patch78: CVE-2022-48565-plistlib-XML-vulns.patch
# PATCH-FIX-UPSTREAM CVE-2023-40217-avoid-ssl-pre-close.patch gh#python/cpython#108315
Patch79: CVE-2023-40217-avoid-ssl-pre-close.patch
# PATCH-FIX-UPSTREAM CVE-2022-48566-compare_digest-more-constant.patch bsc#1214691 mcepl@suse.com
# Make compare_digest more constant-time
Patch80: CVE-2022-48566-compare_digest-more-constant.patch
# PATCH-FIX-OPENSUSE CVE-2023-52425-libexpat-2.6.0-remove-failing-tests.patch bpo#3151 mcepl@suse.com
# We don't have fix for bpo#3151 and it is just not supported
Patch81: CVE-2023-52425-libexpat-2.6.0-remove-failing-tests.patch
# PATCH-FIX-UPSTREAM CVE-2024-0450-zipfile-avoid-quoted-overlap-zipbomb.patch bsc#1221854 mcepl@suse.com
# detecting the vulnerability of the "quoted-overlap" zipbomb (from gh#python/cpython!110016).
Patch82: CVE-2024-0450-zipfile-avoid-quoted-overlap-zipbomb.patch
# COMMON-PATCH-END
BuildRequires: automake
BuildRequires: db-devel
BuildRequires: fdupes
BuildRequires: gdbm-devel
BuildRequires: gmp-devel
BuildRequires: libbz2-devel
# This is NOT switching off NIS support on SLE < 15,
# support for NIS used to be in the glibc itself
%if 0%{?suse_version} >= 1500 && 0%{?suse_version} < 1599
BuildRequires: libnsl-devel
%endif
%if 0%{?suse_version} >= 1500
BuildRequires: libopenssl-1_1-devel
%else
BuildRequires: libopenssl-devel
%endif
BuildRequires: ncurses-devel
BuildRequires: readline-devel
BuildRequires: sqlite-devel
BuildRequires: tk-devel
BuildRequires: xz
BuildRequires: pkgconfig(x11)
# for %%{_datadir}/application and %%{_datadir}/mime/packages
BuildRequires: filesystem
BuildRequires: update-desktop-files
# for %%{_datadir}/icons/hicolor directories
BuildRequires: hicolor-icon-theme
%define python_version %(echo %{tarversion} | head -c 3)
%define idle_name idle
Requires: python-base = %{version}
%if %{suse_version} == 1315 && !0%{?is_opensuse}
Recommends: python-strict-tls-check
%endif
Provides: %{name} = %{python_version}
Provides: python2 = %{version}
# To make older versions of this package to conflict with
# shared-python-startup I need a symbol to conflict with
Provides: python2_split_startup
Obsoletes: python-elementtree
Obsoletes: python-nothreads
Obsoletes: python-sqlite
Obsoletes: python21
# bug437293
%ifarch ppc64
Obsoletes: python-64bit
%endif
BuildRoot: %{_tmppath}/%{name}-%{version}-build
%description
Python is an interpreted, object-oriented programming language, and is
often compared to Tcl, Perl, Scheme, or Java. You can find an overview
of Python in the documentation and tutorials included in the python-doc
(HTML) or python-doc-pdf (PDF) packages.
If you want to install third party modules using distutils, you need to
install python-devel package.
%package idle
Summary: An Integrated Development Environment for Python
Group: Development/Languages/Python
Requires: python-base = %{version}
Requires: python-tk
Provides: python2-idle = %{version}
%description idle
IDLE is a Tkinter based integrated development environment for Python.
It features a multi-window text editor with multiple undo, Python
colorizing, and many other things, as well as a Python shell window and
a debugger.
%package demo
Summary: Python Demonstration Scripts
Group: Development/Languages/Python
Requires: python-base = %{version}
Provides: pyth_dmo
Obsoletes: pyth_dmo
Provides: python2-demo = %{version}
%description demo
Various demonstrations of what you can do with Python and a number of
programs that are useful for building or extending Python.
%package tk
Summary: TkInter - Python Tk Interface
Group: Development/Libraries/Python
Requires: python-base = %{version}
Provides: pyth_tk
Provides: pyth_tkl
Provides: python-tkinter
Provides: python_tkinter_lib
#%ifarch %ix86
#Provides: _tkinter.so
#%endif
Obsoletes: pyth_tk
Obsoletes: pyth_tkl
Obsoletes: python-tkinter
Provides: python2-tk = %{version}
%description tk
Python interface to Tk. Tk is the GUI toolkit that comes with Tcl.
%package curses
Summary: Python Interface to the (N)Curses Library
Group: Development/Libraries/Python
Requires: python-base = %{version}
Obsoletes: pyth_cur
Provides: pyth_cur
Provides: python2-curses = %{version}
%description curses
An easy to use interface to the (n)curses CUI library. CUI stands for
Console User Interface.
%package gdbm
Summary: Python Interface to the GDBM Library
Group: Development/Libraries/Python
Requires: python-base = %{version}
Obsoletes: pygdmod
Provides: pygdmod
Provides: python2-gdbm = %{version}
# Compat to allow BR on python_module dbm and have it properly
# pull in gdbm on py2 and dbm on py3
Provides: python-dbm = %{version}
Provides: python2-dbm = %{version}
%description gdbm
An easy to use interface for GDBM databases. GDBM is the GNU
implementation of the standard Unix DBM databases.
%if %{suse_version} == 1315 && !0%{?is_opensuse}
%package strict-tls-check
Summary: Enable secure verification of TLS certificates
Group: Development/Libraries/Python
Requires: %{name} = %{version}
Supplements: %{name}
%description strict-tls-check
When this package is present, Python performs strict verification of
TLS certificates, including hostname check, by default. This is
the preferred secure setting.
It is distributed as a separate package, because this behavior
can cause verification errors in improperly written legacy scripts
that rely on earlier non-verification behavior.
%endif
%prep
%setup -q -n %{tarname}
# COMMON-PREP-BEGIN
%patch -P 1 -p1
%patch -P 2 -p1
%patch -P 3 -p1
%patch -P 4 -p1
%patch -P 5 -p1
%patch -P 7 -p1
%patch -P 8 -p1
%patch -P 13 -p1
%patch -P 17 -p1
%patch -P 20 -p1
%patch -P 24 -p1
%patch -P 33 -p1
%if %{suse_version} < 1500 && !0%{?is_opensuse}
%patch -P 34 -p1
%endif
%patch -P 35 -p1
%patch -P 38 -p1
%ifarch ppc ppc64 ppc64le
%patch -P 40 -p1
%endif
%patch -P 41 -p1
%if %{suse_version} >= 1500 || (0%{?sle_version} && 0%{?sle_version} >= 120400)
%patch -P 47 -p1
%patch -P 48 -p1
%endif
# SLE-12 needs to skip more
%if %{suse_version} == 1315
%patch -P 57 -p1
%endif
%patch -P 49 -p1
%patch -P 50 -p1
%patch -P 51 -p1
%patch -P 55 -p1
%patch -P 56 -p1
%patch -P 58 -p1
%patch -P 59 -p1
%patch -P 60 -p1
%patch -P 61 -p1
%patch -P 62 -p1
%patch -P 63 -p1
%patch -P 64 -p1
%patch -P 65 -p1
%patch -P 66 -p1
%patch -P 67 -p1
%patch -P 68 -p1
%patch -P 69 -p1
%patch -P 70 -p1
%patch -P 71 -p1
%patch -P 72 -p1
%patch -P 73 -p1
%if 0%{?sle_version} && 0%{?sle_version} < 150000
%patch -P 74 -p1
%endif
%patch -P 75 -p1
%patch -P 76 -p1
%patch -P 77 -p1
%patch -P 78 -p1
%patch -P 79 -p1
%patch -P 80 -p1
%patch -P 81 -p1
%patch -P 82 -p1
# For patch 66
cp -v %{SOURCE66} Lib/test/recursion.tar
# drop Autoconf version requirement
sed -i 's/^version_required/dnl version_required/' configure.ac
# COMMON-PREP-END
%if %{suse_version} == 1315 && !0%{?is_opensuse}
cp %{SOURCE8} Lib/
%endif
%build
%define _lto_cflags %{nil}
# -std=gnu89 option is needed to build with gcc14, bsc#1220970
export OPT="%{optflags} -DOPENSSL_LOAD_CONF -fwrapv -std=gnu89"
autoreconf -f -i . # Modules/_ctypes/libffi
# prevent make from trying to rebuild asdl stuff, which requires existing
# python installation
touch Parser/asdl* Python/Python-ast.c Include/Python-ast.h
%configure \
--docdir=%{_docdir}/python \
--enable-ipv6 \
--with-fpectl \
--enable-shared \
--enable-unicode=ucs4
LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH \
make %{?_smp_mflags}
%check
# on hppa, the threading of glibc is quite broken. The tests just stop
# at some point, and the machine does not build anything more until a
# timeout several hours later.
%ifnarch hppa
# Limit virtual memory to avoid spurious failures
if test $(ulimit -v) = unlimited || test $(ulimit -v) -gt 10000000; then
ulimit -v 10000000 || :
fi
LIST="test_urllib test_ssl test_hashlib test_hmac test_unicodedata test_tarfile test_sqlite test_tcl test_dbm test_anydbm test_dumbdbm test_gdbm test_whichdb test_tk test_ttk_textonly test_bsddb test_bsddb3 test_readline"
make test TESTOPTS="-w $LIST" TESTPYTHONOPTS="-R"
%endif
%install
# replace rest of /usr/local/bin/python or /usr/bin/python2.x with /usr/bin/python
find . -name '*.py' -type f | grep -vE "^./Parser/|^./Python/" \
| xargs grep -lE '^#! *(/usr/.*bin/(env +)?)?python' \
| xargs sed -r -i -e '1s@^#![[:space:]]*(/usr/(local/)?bin/(env +)?)?python([0-9]+\.[0-9]+)?@#!/usr/bin/python@'
# the grep inbetween makes it much faster
########################################
# install it
########################################
%make_install OPT="%{optflags} -fPIC"
########################################
# some cleanups
########################################
# remove hard links and replace them with symlinks
for dir in bin include %{_lib} ; do
rm -f %{buildroot}/%{_prefix}/$dir/python
ln -s python%{python_version} %{buildroot}/%{_prefix}/$dir/python
done
# kill imageop.so, it's insecure
rm -f %{buildroot}/%{_libdir}/python%{python_version}/lib-dynload/imageop.so
#cleanup for -base
rm %{buildroot}%{_bindir}/python%{python_version}
rm %{buildroot}%{_bindir}/python2
rm %{buildroot}%{_bindir}/python
rm %{buildroot}%{_bindir}/smtpd.py
rm %{buildroot}%{_bindir}/pydoc
rm %{buildroot}%{_bindir}/2to3
rm %{buildroot}%{_mandir}/man1/python*
rm %{buildroot}%{_libdir}/libpython*.so.*
rm %{buildroot}%{_libdir}/python
find %{buildroot}%{_libdir}/python%{python_version} -maxdepth 1 \
! \( -name "ssl.py*" -o -name "sle_tls_checks_policy.py*" \) \
-exec rm {} ";"
rm %{buildroot}%{_bindir}/python%{python_version}-config
rm %{buildroot}%{_bindir}/python2-config
rm %{buildroot}%{_bindir}/python-config
rm %{buildroot}%{_libdir}/pkgconfig/*
rm -r %{buildroot}%{_includedir}/python
rm -r %{buildroot}%{_includedir}/python%{python_version}
rm -r %{buildroot}%{_libdir}/python%{python_version}/compiler
rm -r %{buildroot}%{_libdir}/python%{python_version}/config
rm -r %{buildroot}%{_libdir}/python%{python_version}/ctypes
rm -r %{buildroot}%{_libdir}/python%{python_version}/distutils
rm -r %{buildroot}%{_libdir}/python%{python_version}/email
rm -r %{buildroot}%{_libdir}/python%{python_version}/encodings
rm -r %{buildroot}%{_libdir}/python%{python_version}/ensurepip
rm -r %{buildroot}%{_libdir}/python%{python_version}/hotshot
rm -r %{buildroot}%{_libdir}/python%{python_version}/importlib
rm -r %{buildroot}%{_libdir}/python%{python_version}/json
rm -r %{buildroot}%{_libdir}/python%{python_version}/lib2to3
rm -r %{buildroot}%{_libdir}/python%{python_version}/logging
rm -r %{buildroot}%{_libdir}/python%{python_version}/multiprocessing
rm -r %{buildroot}%{_libdir}/python%{python_version}/plat-*
rm -r %{buildroot}%{_libdir}/python%{python_version}/pydoc_data
rm -r %{buildroot}%{_libdir}/python%{python_version}/test
rm -r %{buildroot}%{_libdir}/python%{python_version}/unittest
rm -r %{buildroot}%{_libdir}/python%{python_version}/wsgiref
rm -r %{buildroot}%{_libdir}/python%{python_version}/xml
rm %{buildroot}%{_libdir}/libpython%{python_version}.so
rm %{buildroot}%{_libdir}/python%{python_version}/site-packages/README
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_bisect.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_csv.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_collections.so
rm -f %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_ctypes.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_ctypes_test.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_elementtree.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_functools.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_heapq.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_hotshot.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_io.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_json.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_locale.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_lsprof.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_multiprocessing.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_random.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_socket.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_struct.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_testcapi.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/array.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/binascii.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/bz2.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/cPickle.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/cStringIO.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/cmath.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/crypt.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/datetime.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/fcntl.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/future_builtins.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/grp.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/itertools.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/linuxaudiodev.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/math.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/mmap.so
rm -f %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/nis.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/operator.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/ossaudiodev.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/parser.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/pyexpat.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/resource.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/select.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/spwd.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/strop.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/syslog.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/termios.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/time.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/unicodedata.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/zlib.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_codecs*.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/_multibytecodec.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/audioop.so
rm -f %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/dl.so
rm %{buildroot}%{_libdir}/python%{python_version}/lib-dynload/Python-%{tarversion}-py%{python_version}.egg-info
# replace duplicate .pyo/.pyc with hardlinks
%fdupes %{buildroot}/%{_libdir}/python%{python_version}
########################################
# documentation
########################################
export PDOCS=%{buildroot}%{_docdir}/%{name}
install -d -m 755 $PDOCS
install -c -m 644 %{SOURCE1} $PDOCS/
install -c -m 644 LICENSE $PDOCS/
install -c -m 644 README $PDOCS/
########################################
# tools and demos
########################################
find Tools/ Demo/ -type d \( -regex ".*/.cvsignore" \) -exec rm -f \{\} \;
for x in `find Tools/ Demo/ \( -not -name Makefile \) -print | sort` ; do
test -d $x && ( install -c -m 755 -d $PDOCS/$x ) \
|| ( install -c -m 644 $x $PDOCS/$x )
done
########################################
# idle
########################################
# move idle config into /etc
install -d -m755 %{buildroot}%{_sysconfdir}/%{idle_name}
(
cd %{buildroot}/%{_libdir}/python%{python_version}/idlelib/
for file in *.def ; do
mv $file %{buildroot}%{_sysconfdir}/%{idle_name}/
ln -sf /etc/%{idle_name}/$file %{buildroot}/%{_libdir}/python%{python_version}/idlelib/
done
)
# Install .desktop, mime and appdata files from upstream tarball
%if 0%{?suse_version} >= 1500
install -Dm0644 %{SOURCE50} %{buildroot}/%{_datadir}/mime/packages/idle.appdata.xml
%endif
install -D -m 0644 Lib/idlelib/Icons/idle_16.png %{buildroot}%{_datadir}/icons/hicolor/16x16/apps/idle.png
install -D -m 0644 Lib/idlelib/Icons/idle_32.png %{buildroot}%{_datadir}/icons/hicolor/32x32/apps/idle.png
install -D -m 0644 Lib/idlelib/Icons/idle_48.png %{buildroot}%{_datadir}/icons/hicolor/48x48/apps/idle.png
desktop-file-install --dir=%{buildroot}%{_datadir}/applications %{SOURCE51}
%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig
%files idle
%dir %{_sysconfdir}/%{idle_name}
%config %{_sysconfdir}/%{idle_name}/*
%doc Lib/idlelib/NEWS.txt
%doc Lib/idlelib/README.txt
%doc Lib/idlelib/TODO.txt
%doc Lib/idlelib/extend.txt
%doc Lib/idlelib/ChangeLog
%{_libdir}/python%{python_version}/idlelib
%attr(755, root, root) %{_bindir}/%{idle_name}
%if 0%{?suse_version} >= 1500
%{_datadir}/mime/packages/idle.appdata.xml
%endif
%{_datadir}/applications/idle.desktop
%{_datadir}/icons/hicolor/*/apps/idle.png
%files demo
%doc %{_docdir}/%{name}/Demo
%doc %{_docdir}/%{name}/Tools
%files tk
%{_libdir}/python%{python_version}/lib-tk/
%{_libdir}/python%{python_version}/lib-dynload/_tkinter.so
%files curses
%{_libdir}/python%{python_version}/curses
%{_libdir}/python%{python_version}/lib-dynload/_curses.so
%{_libdir}/python%{python_version}/lib-dynload/_curses_panel.so
%files gdbm
%{_libdir}/python%{python_version}/lib-dynload/gdbm.so
%{_libdir}/python%{python_version}/lib-dynload/dbm.so
%if %{suse_version} == 1315 && !0%{?is_opensuse}
%files strict-tls-check
%{_libdir}/python%{python_version}/sle_tls_checks_policy.py*
%endif
%files
%dir %{_docdir}/%{name}
%doc %{_docdir}/%{name}/README
%doc %{_docdir}/%{name}/LICENSE
%doc %{_docdir}/%{name}/README.SUSE
%dir %{_libdir}/python%{python_version}
%{_libdir}/python%{python_version}/ssl.py*
%{_libdir}/python%{python_version}/bsddb
%{_libdir}/python%{python_version}/sqlite3
%dir %{_libdir}/python%{python_version}/lib-dynload
%{_libdir}/python%{python_version}/lib-dynload/_bsddb.so
%{_libdir}/python%{python_version}/lib-dynload/_hashlib.so
%{_libdir}/python%{python_version}/lib-dynload/_sqlite3.so
%{_libdir}/python%{python_version}/lib-dynload/_ssl.so
%{_libdir}/python%{python_version}/lib-dynload/readline.so
%changelog

BIN
recursion.tar Normal file

Binary file not shown.

View File

@ -0,0 +1,53 @@
---
Makefile.pre.in | 26 +-------------------------
1 file changed, 1 insertion(+), 25 deletions(-)
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -488,7 +488,7 @@ coverage-report: regen-grammar
# Build the interpreter
-$(BUILDPYTHON): Modules/python.o $(LIBRARY) $(LDLIBRARY)
+$(BUILDPYTHON): Modules/python.o $(LDLIBRARY)
$(LINKCC) $(LDFLAGS) $(LINKFORSHARED) -o $@ \
Modules/python.o \
$(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST)
@@ -529,18 +529,6 @@ sharedmods: $(BUILDPYTHON) pybuilddir.tx
_TCLTK_INCLUDES='$(TCLTK_INCLUDES)' _TCLTK_LIBS='$(TCLTK_LIBS)' \
$(PYTHON_FOR_BUILD) $(srcdir)/setup.py $$quiet build
-# Build static library
-# avoid long command lines, same as LIBRARY_OBJS
-$(LIBRARY): $(LIBRARY_OBJS)
- -rm -f $@
- $(AR) $(ARFLAGS) $@ Modules/getbuildinfo.o
- $(AR) $(ARFLAGS) $@ $(PARSER_OBJS)
- $(AR) $(ARFLAGS) $@ $(OBJECT_OBJS)
- $(AR) $(ARFLAGS) $@ $(PYTHON_OBJS)
- $(AR) $(ARFLAGS) $@ $(MODULE_OBJS) $(SIGNAL_OBJS)
- $(AR) $(ARFLAGS) $@ $(MODOBJS)
- $(RANLIB) $@
-
libpython$(VERSION).so: $(LIBRARY_OBJS)
if test $(INSTSONAME) != $(LDLIBRARY); then \
$(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \
@@ -1226,18 +1214,6 @@ libainstall: @DEF_MAKE_RULE@ python-conf
else true; \
fi; \
done
- @if test -d $(LIBRARY); then :; else \
- if test "$(PYTHONFRAMEWORKDIR)" = no-framework; then \
- if test "$(SO)" = .dll; then \
- $(INSTALL_DATA) $(LDLIBRARY) $(DESTDIR)$(LIBPL) ; \
- else \
- $(INSTALL_DATA) $(LIBRARY) $(DESTDIR)$(LIBPL)/$(LIBRARY) ; \
- $(RANLIB) $(DESTDIR)$(LIBPL)/$(LIBRARY) ; \
- fi; \
- else \
- echo Skip install of $(LIBRARY) - use make frameworkinstall; \
- fi; \
- fi
$(INSTALL_DATA) Modules/config.c $(DESTDIR)$(LIBPL)/config.c
$(INSTALL_DATA) Modules/python.o $(DESTDIR)$(LIBPL)/python.o
$(INSTALL_DATA) $(srcdir)/Modules/config.c.in $(DESTDIR)$(LIBPL)/config.c.in

15
reproducible.patch Normal file
View File

@ -0,0 +1,15 @@
Index: Python-2.7.13/Lib/py_compile.py
===================================================================
--- Python-2.7.13.orig/Lib/py_compile.py
+++ Python-2.7.13/Lib/py_compile.py
@@ -108,6 +108,10 @@ def compile(file, cfile=None, dfile=None
timestamp = long(os.fstat(f.fileno()).st_mtime)
except AttributeError:
timestamp = long(os.stat(file).st_mtime)
+ sde = os.environ.get('SOURCE_DATE_EPOCH')
+ if sde and timestamp > int(sde):
+ timestamp = int(sde)
+ os.utime(file, (timestamp, timestamp))
codestring = f.read()
try:
codeobject = __builtin__.compile(codestring, dfile or file,'exec')

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2e98dac61759aa12d18e6e6f4c6c582095882f08431259ee92845b47a8378ff0
size 583522

View File

@ -0,0 +1,19 @@
---
Lib/test/test_ssl.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -1344,9 +1344,9 @@ class ContextTests(unittest.TestCase):
extra_env = {}
# Omitting it leaves verification on
assert_python_ok("-c", https_is_verified, **extra_env)
- # Setting it to zero turns verification off
- extra_env[ssl._https_verify_envvar] = "0"
- assert_python_ok("-c", https_is_not_verified, **extra_env)
+ ## # Setting it to zero turns verification off
+ ## extra_env[ssl._https_verify_envvar] = "0"
+ ## assert_python_ok("-c", https_is_not_verified, **extra_env)
# Any other value should also leave it on
for setting in ("", "1", "enabled", "foo"):
extra_env[ssl._https_verify_envvar] = setting

4
sle_tls_checks_policy.py Normal file
View File

@ -0,0 +1,4 @@
import ssl
def get_policy():
return ssl.create_default_context

View File

@ -0,0 +1,40 @@
--- a/Doc/tools/extensions/pyspecific.py
+++ b/Doc/tools/extensions/pyspecific.py
@@ -103,7 +103,11 @@ class ImplementationDetail(Directive):
# Support for documenting decorators
from sphinx import addnodes
-from sphinx.domains.python import PyModulelevel, PyClassmember
+try:
+ from sphinx.domains.python import PyFunction, PyMethod
+except ImportError:
+ from sphinx.domains.python import PyClassmember as PyMethod
+ from sphinx.domains.python import PyModulelevel as PyFunction
class PyDecoratorMixin(object):
def handle_signature(self, sig, signode):
@@ -114,16 +118,16 @@ class PyDecoratorMixin(object):
def needs_arglist(self):
return False
-class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel):
+class PyDecoratorFunction(PyDecoratorMixin, PyFunction):
def run(self):
# a decorator function is a function after all
self.name = 'py:function'
- return PyModulelevel.run(self)
+ return PyFunction.run(self)
-class PyDecoratorMethod(PyDecoratorMixin, PyClassmember):
+class PyDecoratorMethod(PyDecoratorMixin, PyMethod):
def run(self):
self.name = 'py:method'
- return PyClassmember.run(self)
+ return PyMethod.run(self)
# Support for building "topic help" for pydoc
--- /dev/null
+++ b/Misc/NEWS.d/next/Documentation/2020-09-12-17-37-13.bpo-35293._cOwPD.rst
@@ -0,0 +1 @@
+Fix RemovedInSphinx40Warning when building the documentation. Patch by Dong-hee Na.