mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
121 lines
5.4 KiB
Python
121 lines
5.4 KiB
Python
import os
|
|
import shutil
|
|
import tempfile
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
from django.test import TestCase
|
|
from django.urls import reverse
|
|
from django.contrib.auth import get_user_model
|
|
|
|
from computes.models import Compute
|
|
from storages.views import handle_uploaded_file
|
|
from vrtManager.connection import CONN_SOCKET, CONN_SSH
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class StorageUploadSecurityTests(TestCase):
|
|
def setUp(self):
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
self.mock_socket_conn = MagicMock()
|
|
self.mock_socket_conn.conn = CONN_SOCKET
|
|
|
|
self.mock_ssh_conn = MagicMock()
|
|
self.mock_ssh_conn.conn = CONN_SSH
|
|
self.mock_ssh_conn.host = "127.0.0.1"
|
|
self.mock_ssh_conn.login = "test"
|
|
self.mock_ssh_conn.passwd = "test"
|
|
|
|
def tearDown(self):
|
|
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
|
|
|
def test_handle_uploaded_file_invalid_names(self):
|
|
dummy_chunk = SimpleUploadedFile("test.iso", b"data")
|
|
invalid_names = ["", " ", ".", "..", "test\x00evil.iso", "../evil.iso", "../../etc/shadow"]
|
|
|
|
for bad_name in invalid_names:
|
|
clean_base = os.path.basename(bad_name).strip()
|
|
# bad_name either gets rejected as empty/dot/null or has basename extracted
|
|
if not clean_base or clean_base in (".", "..") or "\x00" in clean_base:
|
|
with self.assertRaises(ValueError):
|
|
handle_uploaded_file(self.mock_socket_conn, self.temp_dir, bad_name, dummy_chunk, is_last_chunk=True)
|
|
|
|
def test_handle_uploaded_file_successful_socket_upload(self):
|
|
dummy_chunk = SimpleUploadedFile("install.iso", b"ISODATA_BLOCK_123")
|
|
file_name = "install.iso"
|
|
|
|
# Chunk 1 (is_last_chunk=False)
|
|
handle_uploaded_file(self.mock_socket_conn, self.temp_dir, file_name, dummy_chunk, is_last_chunk=False)
|
|
part_path = os.path.join(self.temp_dir, f"{file_name}.part")
|
|
final_path = os.path.join(self.temp_dir, file_name)
|
|
self.assertTrue(os.path.exists(part_path))
|
|
self.assertFalse(os.path.exists(final_path))
|
|
|
|
# Chunk 2 (is_last_chunk=True)
|
|
dummy_chunk2 = SimpleUploadedFile("install.iso", b"_PART2")
|
|
handle_uploaded_file(self.mock_socket_conn, self.temp_dir, file_name, dummy_chunk2, is_last_chunk=True)
|
|
self.assertFalse(os.path.exists(part_path))
|
|
self.assertTrue(os.path.exists(final_path))
|
|
|
|
with open(final_path, "rb") as f:
|
|
self.assertEqual(f.read(), b"ISODATA_BLOCK_123_PART2")
|
|
|
|
def test_handle_uploaded_file_jail_containment(self):
|
|
dummy_chunk = SimpleUploadedFile("test.iso", b"data")
|
|
# Attempt traversal outside sandbox
|
|
with patch("os.path.basename", side_effect=lambda x: x): # simulate raw traversal name
|
|
with self.assertRaises(PermissionError):
|
|
handle_uploaded_file(self.mock_socket_conn, self.temp_dir, "../../../evil.iso", dummy_chunk, is_last_chunk=True)
|
|
|
|
def test_handle_uploaded_file_ssh_traversal_check(self):
|
|
dummy_chunk = SimpleUploadedFile("test.iso", b"data")
|
|
with patch("os.path.basename", side_effect=lambda x: x):
|
|
with self.assertRaises(PermissionError):
|
|
handle_uploaded_file(self.mock_ssh_conn, "/var/lib/libvirt/images", "../../../evil.iso", dummy_chunk, is_last_chunk=True)
|
|
|
|
|
|
class StorageUploadViewTests(TestCase):
|
|
def setUp(self):
|
|
self.admin = User.objects.create_superuser(username="storage_admin", password="password", email="storage_admin@example.com")
|
|
self.client.login(username="storage_admin", password="password")
|
|
self.compute = Compute.objects.create(name="LocalCompute", hostname="localhost", type=1, login="u", password="p")
|
|
|
|
@patch("storages.views.wvmStorage")
|
|
def test_iso_upload_missing_chunk(self, mock_wvm):
|
|
mock_conn = MagicMock()
|
|
mock_conn.get_status.return_value = 1
|
|
mock_conn.get_target_path.return_value = "/tmp"
|
|
mock_conn.get_type.return_value = "dir"
|
|
mock_conn.get_autostart.return_value = 1
|
|
mock_conn.get_size.return_value = [1000, 500]
|
|
mock_conn.update_volumes.return_value = []
|
|
mock_wvm.return_value = mock_conn
|
|
|
|
url = reverse("storage", args=[self.compute.id, "default"])
|
|
res = self.client.post(url, {"iso_upload": "1", "file_name": "test.iso"})
|
|
self.assertEqual(res.status_code, 400)
|
|
self.assertIn("No file chunk was submitted", res.json().get("error", ""))
|
|
|
|
@patch("storages.views.wvmStorage")
|
|
def test_iso_upload_invalid_filename(self, mock_wvm):
|
|
mock_conn = MagicMock()
|
|
mock_conn.get_status.return_value = 1
|
|
mock_conn.get_target_path.return_value = "/tmp"
|
|
mock_conn.get_type.return_value = "dir"
|
|
mock_conn.get_autostart.return_value = 1
|
|
mock_conn.get_size.return_value = [1000, 500]
|
|
mock_conn.update_volumes.return_value = []
|
|
mock_wvm.return_value = mock_conn
|
|
|
|
url = reverse("storage", args=[self.compute.id, "default"])
|
|
chunk = SimpleUploadedFile("test.iso", b"chunkdata")
|
|
res = self.client.post(url, {
|
|
"iso_upload": "1",
|
|
"file": chunk,
|
|
"file_name": "..\x00..",
|
|
"chunk_index": "0",
|
|
"total_chunks": "1",
|
|
})
|
|
self.assertEqual(res.status_code, 400)
|
|
self.assertIn("Invalid file name", res.json().get("error", ""))
|