Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Meta python merge 04/21/2021 #342

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ HOMEPAGE = "https://github.com/ilanschnell/bitarray"
LICENSE = "PSF"
LIC_FILES_CHKSUM = "file://PKG-INFO;beginline=8;endline=8;md5=2ad702cdcd49e8d2ac01d7e7d0810d2d"

SRC_URI[sha256sum] = "d7a49d21ae04c5af195023b140800186ebf208e3a4fc5b21a1389531cb7a7170"
SRC_URI[sha256sum] = "ee13850d3237c254c7af8acce2f1a044a1c4f22dcec5380cba7443c8be38f701"

inherit setuptools3 pypi

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=bf405a8056a6647e7d077b0e7bc36aba"

LDSHARED += "-pthread"

SRC_URI[sha256sum] = "7e177e4bea2de937a584b13645cab32f25e3d96fc0bc4a4cf99c27dc77682be6"
SRC_URI[sha256sum] = "5a60d3780149e13b7a6ff7ad6526b38846354d11a15e21068e57073e29e19bed"

SRC_URI += " \
file://run-ptest \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
From 4036d62bda0e9e9f6172943794b744a454ca49c2 Mon Sep 17 00:00:00 2001
From: Mariusz Felisiak <[email protected]>
Date: Tue, 16 Mar 2021 10:19:00 +0100
Subject: [PATCH] Fixed CVE-2021-28658 -- Fixed potential directory-traversal
via uploaded files.

Thanks Claude Paroz for the initial patch.
Thanks Dennis Brinkrolf for the report.

Backport of d4d800ca1addc4141e03c5440a849bb64d1582cd from main.

Upstream-Status: Backport
CVE: CVE-2021-28658

Reference to upstream patch:
[https://github.com/django/django/commit/4036d62bda0e9e9f6172943794b744a454ca49c2]

[SG: Adapted stable/2.2.x patch for 2.2.16]
Signed-off-by: Stefan Ghinea <[email protected]>
---
django/http/multipartparser.py | 13 ++++--
docs/releases/2.2.16.txt | 12 +++++
tests/file_uploads/tests.py | 72 ++++++++++++++++++++++-------
tests/file_uploads/uploadhandler.py | 31 +++++++++++++
tests/file_uploads/urls.py | 1 +
tests/file_uploads/views.py | 12 ++++-
6 files changed, 120 insertions(+), 21 deletions(-)

diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py
index f6f12ca..5a9cca8 100644
--- a/django/http/multipartparser.py
+++ b/django/http/multipartparser.py
@@ -7,6 +7,7 @@ file upload handlers for processing.
import base64
import binascii
import cgi
+import os
from urllib.parse import unquote

from django.conf import settings
@@ -205,7 +206,7 @@ class MultiPartParser:
file_name = disposition.get('filename')
if file_name:
file_name = force_text(file_name, encoding, errors='replace')
- file_name = self.IE_sanitize(unescape_entities(file_name))
+ file_name = self.sanitize_file_name(file_name)
if not file_name:
continue

@@ -293,9 +294,13 @@ class MultiPartParser:
self._files.appendlist(force_text(old_field_name, self._encoding, errors='replace'), file_obj)
break

- def IE_sanitize(self, filename):
- """Cleanup filename from Internet Explorer full paths."""
- return filename and filename[filename.rfind("\\") + 1:].strip()
+ def sanitize_file_name(self, file_name):
+ file_name = unescape_entities(file_name)
+ # Cleanup Windows-style path separators.
+ file_name = file_name[file_name.rfind('\\') + 1:].strip()
+ return os.path.basename(file_name)
+
+ IE_sanitize = sanitize_file_name

def _close_files(self):
# Free up all file handles.
diff --git a/docs/releases/2.2.16.txt b/docs/releases/2.2.16.txt
index 31231fb..4b7021b 100644
--- a/docs/releases/2.2.16.txt
+++ b/docs/releases/2.2.16.txt
@@ -2,6 +2,18 @@
Django 2.2.16 release notes
===========================

+*April 6, 2021*
+
+Backported from Django 2.2.20 a fix for a security issue.
+
+CVE-2021-28658: Potential directory-traversal via uploaded files
+================================================================
+
+``MultiPartParser`` allowed directory-traversal via uploaded files with
+suitably crafted file names.
+
+Built-in upload handlers were not affected by this vulnerability.
+
*September 1, 2020*

Django 2.2.16 fixes two security issues and two data loss bugs in 2.2.15.
diff --git a/tests/file_uploads/tests.py b/tests/file_uploads/tests.py
index ea4976d..2a08d1b 100644
--- a/tests/file_uploads/tests.py
+++ b/tests/file_uploads/tests.py
@@ -22,6 +22,21 @@ UNICODE_FILENAME = 'test-0123456789_中文_Orléans.jpg'
MEDIA_ROOT = sys_tempfile.mkdtemp()
UPLOAD_TO = os.path.join(MEDIA_ROOT, 'test_upload')

+CANDIDATE_TRAVERSAL_FILE_NAMES = [
+ '/tmp/hax0rd.txt', # Absolute path, *nix-style.
+ 'C:\\Windows\\hax0rd.txt', # Absolute path, win-style.
+ 'C:/Windows/hax0rd.txt', # Absolute path, broken-style.
+ '\\tmp\\hax0rd.txt', # Absolute path, broken in a different way.
+ '/tmp\\hax0rd.txt', # Absolute path, broken by mixing.
+ 'subdir/hax0rd.txt', # Descendant path, *nix-style.
+ 'subdir\\hax0rd.txt', # Descendant path, win-style.
+ 'sub/dir\\hax0rd.txt', # Descendant path, mixed.
+ '../../hax0rd.txt', # Relative path, *nix-style.
+ '..\\..\\hax0rd.txt', # Relative path, win-style.
+ '../..\\hax0rd.txt', # Relative path, mixed.
+ '..&#x2F;hax0rd.txt', # HTML entities.
+]
+

@override_settings(MEDIA_ROOT=MEDIA_ROOT, ROOT_URLCONF='file_uploads.urls', MIDDLEWARE=[])
class FileUploadTests(TestCase):
@@ -205,22 +220,8 @@ class FileUploadTests(TestCase):
# a malicious payload with an invalid file name (containing os.sep or
# os.pardir). This similar to what an attacker would need to do when
# trying such an attack.
- scary_file_names = [
- "/tmp/hax0rd.txt", # Absolute path, *nix-style.
- "C:\\Windows\\hax0rd.txt", # Absolute path, win-style.
- "C:/Windows/hax0rd.txt", # Absolute path, broken-style.
- "\\tmp\\hax0rd.txt", # Absolute path, broken in a different way.
- "/tmp\\hax0rd.txt", # Absolute path, broken by mixing.
- "subdir/hax0rd.txt", # Descendant path, *nix-style.
- "subdir\\hax0rd.txt", # Descendant path, win-style.
- "sub/dir\\hax0rd.txt", # Descendant path, mixed.
- "../../hax0rd.txt", # Relative path, *nix-style.
- "..\\..\\hax0rd.txt", # Relative path, win-style.
- "../..\\hax0rd.txt" # Relative path, mixed.
- ]
-
payload = client.FakePayload()
- for i, name in enumerate(scary_file_names):
+ for i, name in enumerate(CANDIDATE_TRAVERSAL_FILE_NAMES):
payload.write('\r\n'.join([
'--' + client.BOUNDARY,
'Content-Disposition: form-data; name="file%s"; filename="%s"' % (i, name),
@@ -240,7 +241,7 @@ class FileUploadTests(TestCase):
response = self.client.request(**r)
# The filenames should have been sanitized by the time it got to the view.
received = response.json()
- for i, name in enumerate(scary_file_names):
+ for i, name in enumerate(CANDIDATE_TRAVERSAL_FILE_NAMES):
got = received["file%s" % i]
self.assertEqual(got, "hax0rd.txt")

@@ -518,6 +519,36 @@ class FileUploadTests(TestCase):
# shouldn't differ.
self.assertEqual(os.path.basename(obj.testfile.path), 'MiXeD_cAsE.txt')

+ def test_filename_traversal_upload(self):
+ os.makedirs(UPLOAD_TO, exist_ok=True)
+ self.addCleanup(shutil.rmtree, MEDIA_ROOT)
+ file_name = '..&#x2F;test.txt',
+ payload = client.FakePayload()
+ payload.write(
+ '\r\n'.join([
+ '--' + client.BOUNDARY,
+ 'Content-Disposition: form-data; name="my_file"; '
+ 'filename="%s";' % file_name,
+ 'Content-Type: text/plain',
+ '',
+ 'file contents.\r\n',
+ '\r\n--' + client.BOUNDARY + '--\r\n',
+ ]),
+ )
+ r = {
+ 'CONTENT_LENGTH': len(payload),
+ 'CONTENT_TYPE': client.MULTIPART_CONTENT,
+ 'PATH_INFO': '/upload_traversal/',
+ 'REQUEST_METHOD': 'POST',
+ 'wsgi.input': payload,
+ }
+ response = self.client.request(**r)
+ result = response.json()
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(result['file_name'], 'test.txt')
+ self.assertIs(os.path.exists(os.path.join(MEDIA_ROOT, 'test.txt')), False)
+ self.assertIs(os.path.exists(os.path.join(UPLOAD_TO, 'test.txt')), True)
+

@override_settings(MEDIA_ROOT=MEDIA_ROOT)
class DirectoryCreationTests(SimpleTestCase):
@@ -591,6 +622,15 @@ class MultiParserTests(SimpleTestCase):
}, StringIO('x'), [], 'utf-8')
self.assertEqual(multipart_parser._content_length, 0)

+ def test_sanitize_file_name(self):
+ parser = MultiPartParser({
+ 'CONTENT_TYPE': 'multipart/form-data; boundary=_foo',
+ 'CONTENT_LENGTH': '1'
+ }, StringIO('x'), [], 'utf-8')
+ for file_name in CANDIDATE_TRAVERSAL_FILE_NAMES:
+ with self.subTest(file_name=file_name):
+ self.assertEqual(parser.sanitize_file_name(file_name), 'hax0rd.txt')
+
def test_rfc2231_parsing(self):
test_data = (
(b"Content-Type: application/x-stuff; title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A",
diff --git a/tests/file_uploads/uploadhandler.py b/tests/file_uploads/uploadhandler.py
index 7c6199f..65d70c6 100644
--- a/tests/file_uploads/uploadhandler.py
+++ b/tests/file_uploads/uploadhandler.py
@@ -1,6 +1,8 @@
"""
Upload handlers to test the upload API.
"""
+import os
+from tempfile import NamedTemporaryFile

from django.core.files.uploadhandler import FileUploadHandler, StopUpload

@@ -35,3 +37,32 @@ class ErroringUploadHandler(FileUploadHandler):
"""A handler that raises an exception."""
def receive_data_chunk(self, raw_data, start):
raise CustomUploadError("Oops!")
+
+
+class TraversalUploadHandler(FileUploadHandler):
+ """A handler with potential directory-traversal vulnerability."""
+ def __init__(self, request=None):
+ from .views import UPLOAD_TO
+
+ super().__init__(request)
+ self.upload_dir = UPLOAD_TO
+
+ def file_complete(self, file_size):
+ self.file.seek(0)
+ self.file.size = file_size
+ with open(os.path.join(self.upload_dir, self.file_name), 'wb') as fp:
+ fp.write(self.file.read())
+ return self.file
+
+ def new_file(
+ self, field_name, file_name, content_type, content_length, charset=None,
+ content_type_extra=None,
+ ):
+ super().new_file(
+ file_name, file_name, content_length, content_length, charset,
+ content_type_extra,
+ )
+ self.file = NamedTemporaryFile(suffix='.upload', dir=self.upload_dir)
+
+ def receive_data_chunk(self, raw_data, start):
+ self.file.write(raw_data)
diff --git a/tests/file_uploads/urls.py b/tests/file_uploads/urls.py
index 3e7985d..eaac1da 100644
--- a/tests/file_uploads/urls.py
+++ b/tests/file_uploads/urls.py
@@ -4,6 +4,7 @@ from . import views

urlpatterns = [
path('upload/', views.file_upload_view),
+ path('upload_traversal/', views.file_upload_traversal_view),
path('verify/', views.file_upload_view_verify),
path('unicode_name/', views.file_upload_unicode_name),
path('echo/', views.file_upload_echo),
diff --git a/tests/file_uploads/views.py b/tests/file_uploads/views.py
index d4947e4..137c6f3 100644
--- a/tests/file_uploads/views.py
+++ b/tests/file_uploads/views.py
@@ -6,7 +6,9 @@ from django.http import HttpResponse, HttpResponseServerError, JsonResponse

from .models import FileModel
from .tests import UNICODE_FILENAME, UPLOAD_TO
-from .uploadhandler import ErroringUploadHandler, QuotaUploadHandler
+from .uploadhandler import (
+ ErroringUploadHandler, QuotaUploadHandler, TraversalUploadHandler,
+)


def file_upload_view(request):
@@ -158,3 +160,11 @@ def file_upload_fd_closing(request, access):
if access == 't':
request.FILES # Trigger file parsing.
return HttpResponse('')
+
+
+def file_upload_traversal_view(request):
+ request.upload_handlers.insert(0, TraversalUploadHandler())
+ request.FILES # Trigger file parsing.
+ return JsonResponse(
+ {'file_name': request.upload_handlers[0].file_name},
+ )
--
2.17.1

2 changes: 2 additions & 0 deletions meta-python/recipes-devtools/python/python3-django_2.2.16.bb
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ SRC_URI[sha256sum] = "62cf45e5ee425c52e411c0742e641a6588b7e8af0d2c274a27940931b2
RDEPENDS_${PN} += "\
${PYTHON_PN}-sqlparse \
"
SRC_URI += "file://CVE-2021-28658.patch \
"
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ SECTION = "devel/python"
LICENSE = "BSD-2-Clause & GPL-2.0 & Python-2.0"
LIC_FILES_CHKSUM = "file://COPYING.txt;md5=836a1950177996968a49ff477a4a61c4"

SRC_URI[sha256sum] = "e2ffeea817964356ba4470efba7c2f42b6b0de0b04e66378507e3e2504bbff4c"
SRC_URI[sha256sum] = "686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"

inherit pypi setuptools3
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
Fixed host contamination of include files
From 8abf79f77d9151d6786da3c8e868117822bce6d4 Mon Sep 17 00:00:00 2001
From: Khem Raj <[email protected]>
Date: Fri, 16 Apr 2021 10:48:36 -0700
Subject: [PATCH] Fix host contamination of include files

python3-icu-2.5-r0 do_package_qa: QA Issue: python3-icu: The compile log indicates that host include and/or library paths were used.

Also, don't use icu-config

Upstream-Status: [inappropriate] OE specific
Upstream-Status: Inappropriate [OE specific]
Signed-off-by: Armin Kuster <[email protected]>
Signed-off-by: Khem Raj <[email protected]>
---
setup.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

Index: PyICU-2.5/setup.py
===================================================================
--- PyICU-2.5.orig/setup.py
+++ PyICU-2.5/setup.py
@@ -81,7 +81,7 @@ Building PyICU %s for ICU %s (max ICU ma
diff --git a/setup.py b/setup.py
index e647db4..abed399 100644
--- a/setup.py
+++ b/setup.py
@@ -81,7 +81,7 @@ Building PyICU %s for ICU %s (max ICU major version supported: %s)

CONFIGURE_WITH_ICU_CONFIG = {
'darwin': True,
Expand All @@ -20,11 +27,14 @@ Index: PyICU-2.5/setup.py
'freebsd': False, # not tested
'win32': False, # no icu-config
'sunos5': False, # not tested
@@ -274,7 +274,6 @@ setup(name="PyICU",
@@ -267,7 +267,6 @@ setup(name="PyICU",
ext_modules=[Extension('_icu',
[filename for filename in sorted(os.listdir(os.curdir))
if filename.endswith('.cpp')],
- include_dirs=_includes,
extra_compile_args=_cflags,
extra_link_args=_lflags,
libraries=_libraries)],
--
2.31.1

13 changes: 0 additions & 13 deletions meta-python/recipes-devtools/python/python3-icu_2.6.bb

This file was deleted.

13 changes: 13 additions & 0 deletions meta-python/recipes-devtools/python/python3-icu_2.7.2.bb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
SUMMARY = "Python extension wrapping the ICU C++ API"
SECTION = "devel/python"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://setup.py;beginline=244;endline=252;md5=3e00c41c6d04310707992b93307a224f"

DEPENDS += "pkgconfig icu"

PYPI_PACKAGE = "PyICU"
SRC_URI[sha256sum] = "1382869b22d91cc99274f9b525fa7d9199b44d9007ff0036a09747839a01e9dc"

SRC_URI += "file://0001-Fix-host-contamination-of-include-files.patch"

inherit pypi setuptools3
Loading