Blame SOURCES/pip-directory-traversal-security-issue-tests.patch

bd5b0e
From 7917dbda14ef64a5e7fdea48383a266577484ac8 Mon Sep 17 00:00:00 2001
bd5b0e
From: Tomas Orsava <torsava@redhat.com>
bd5b0e
Date: Wed, 19 Aug 2020 12:51:16 +0200
bd5b0e
Subject: [PATCH 2/2] FIX #6413 pip install <url> allow directory traversal
bd5b0e
 (tests)
bd5b0e
bd5b0e
---
bd5b0e
 tests/unit/test_download.py | 85 +++++++++++++++++++++++++++++++++++++
bd5b0e
 1 file changed, 85 insertions(+)
bd5b0e
bd5b0e
diff --git a/tests/unit/test_download.py b/tests/unit/test_download.py
bd5b0e
index ee4b11c..15f99ec 100644
bd5b0e
--- a/tests/unit/test_download.py
bd5b0e
+++ b/tests/unit/test_download.py
bd5b0e
@@ -1,5 +1,6 @@
bd5b0e
 import hashlib
bd5b0e
 import os
bd5b0e
+import sys
bd5b0e
 from io import BytesIO
bd5b0e
 from shutil import rmtree, copy
bd5b0e
 from tempfile import mkdtemp
bd5b0e
@@ -13,6 +14,7 @@ import pip
bd5b0e
 from pip.exceptions import HashMismatch
bd5b0e
 from pip.download import (
bd5b0e
     PipSession, SafeFileCache, path_to_url, unpack_http_url, url_to_path,
bd5b0e
+    _download_http_url, parse_content_disposition, sanitize_content_filename,
bd5b0e
     unpack_file_url,
bd5b0e
 )
bd5b0e
 from pip.index import Link
bd5b0e
@@ -123,6 +125,89 @@ def test_unpack_http_url_bad_downloaded_checksum(mock_unpack_file):
bd5b0e
         rmtree(download_dir)
bd5b0e
 
bd5b0e
 
bd5b0e
+@pytest.mark.parametrize("filename, expected", [
bd5b0e
+    ('dir/file', 'file'),
bd5b0e
+    ('../file', 'file'),
bd5b0e
+    ('../../file', 'file'),
bd5b0e
+    ('../', ''),
bd5b0e
+    ('../..', '..'),
bd5b0e
+    ('/', ''),
bd5b0e
+])
bd5b0e
+def test_sanitize_content_filename(filename, expected):
bd5b0e
+    """
bd5b0e
+    Test inputs where the result is the same for Windows and non-Windows.
bd5b0e
+    """
bd5b0e
+    assert sanitize_content_filename(filename) == expected
bd5b0e
+
bd5b0e
+
bd5b0e
+@pytest.mark.parametrize("filename, win_expected, non_win_expected", [
bd5b0e
+    ('dir\\file', 'file', 'dir\\file'),
bd5b0e
+    ('..\\file', 'file', '..\\file'),
bd5b0e
+    ('..\\..\\file', 'file', '..\\..\\file'),
bd5b0e
+    ('..\\', '', '..\\'),
bd5b0e
+    ('..\\..', '..', '..\\..'),
bd5b0e
+    ('\\', '', '\\'),
bd5b0e
+])
bd5b0e
+def test_sanitize_content_filename__platform_dependent(
bd5b0e
+    filename,
bd5b0e
+    win_expected,
bd5b0e
+    non_win_expected
bd5b0e
+):
bd5b0e
+    """
bd5b0e
+    Test inputs where the result is different for Windows and non-Windows.
bd5b0e
+    """
bd5b0e
+    if sys.platform == 'win32':
bd5b0e
+        expected = win_expected
bd5b0e
+    else:
bd5b0e
+        expected = non_win_expected
bd5b0e
+    assert sanitize_content_filename(filename) == expected
bd5b0e
+
bd5b0e
+
bd5b0e
+@pytest.mark.parametrize("content_disposition, default_filename, expected", [
bd5b0e
+    ('attachment;filename="../file"', 'df', 'file'),
bd5b0e
+])
bd5b0e
+def test_parse_content_disposition(
bd5b0e
+    content_disposition,
bd5b0e
+    default_filename,
bd5b0e
+    expected
bd5b0e
+):
bd5b0e
+    actual = parse_content_disposition(content_disposition, default_filename)
bd5b0e
+    assert actual == expected
bd5b0e
+
bd5b0e
+
bd5b0e
+def test_download_http_url__no_directory_traversal(tmpdir):
bd5b0e
+    """
bd5b0e
+    Test that directory traversal doesn't happen on download when the
bd5b0e
+    Content-Disposition header contains a filename with a ".." path part.
bd5b0e
+    """
bd5b0e
+    mock_url = 'http://www.example.com/whatever.tgz'
bd5b0e
+    contents = b'downloaded'
bd5b0e
+    link = Link(mock_url)
bd5b0e
+
bd5b0e
+    session = Mock()
bd5b0e
+    resp = MockResponse(contents)
bd5b0e
+    resp.url = mock_url
bd5b0e
+    resp.headers = {
bd5b0e
+        # Set the content-type to a random value to prevent
bd5b0e
+        # mimetypes.guess_extension from guessing the extension.
bd5b0e
+        'content-type': 'random',
bd5b0e
+        'content-disposition': 'attachment;filename="../out_dir_file"'
bd5b0e
+    }
bd5b0e
+    session.get.return_value = resp
bd5b0e
+
bd5b0e
+    download_dir = tmpdir.join('download')
bd5b0e
+    os.mkdir(download_dir)
bd5b0e
+    file_path, content_type = _download_http_url(
bd5b0e
+        link,
bd5b0e
+        session,
bd5b0e
+        download_dir,
bd5b0e
+        hashes=None,
bd5b0e
+    )
bd5b0e
+    # The file should be downloaded to download_dir.
bd5b0e
+    actual = os.listdir(download_dir)
bd5b0e
+    assert actual == ['out_dir_file']
bd5b0e
+
bd5b0e
+
bd5b0e
 @pytest.mark.skipif("sys.platform == 'win32'")
bd5b0e
 def test_path_to_url_unix():
bd5b0e
     assert path_to_url('/tmp/file') == 'file:///tmp/file'
bd5b0e
-- 
bd5b0e
2.25.4
bd5b0e