98 lines
3.8 KiB
Python
98 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
import zipfile
|
|
|
|
try:
|
|
import pyzipper
|
|
except ImportError: # pragma: no cover
|
|
pyzipper = None
|
|
|
|
from govoplan_campaign.backend.services.zip_service import (
|
|
create_zip_archive,
|
|
zip_archive_evidence,
|
|
)
|
|
|
|
|
|
class ZipServiceTests(unittest.TestCase):
|
|
def test_standard_password_zip_uses_zipcrypto_readable_by_stdlib(self) -> None:
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
root = Path(temp_dir)
|
|
source = root / "source.txt"
|
|
source.write_text("Hello Windows ZIP", encoding="utf-8")
|
|
output = root / "standard.zip"
|
|
|
|
create_zip_archive(output, [(source, "message.txt")], "secret", "zip_standard")
|
|
|
|
with zipfile.ZipFile(output) as archive:
|
|
info = archive.getinfo("message.txt")
|
|
self.assertEqual(info.compress_type, zipfile.ZIP_DEFLATED)
|
|
self.assertTrue(info.flag_bits & 0x1)
|
|
self.assertEqual(archive.read("message.txt", pwd=b"secret"), b"Hello Windows ZIP")
|
|
with self.assertRaises(RuntimeError):
|
|
archive.read("message.txt", pwd=b"wrong-password")
|
|
|
|
evidence = zip_archive_evidence(
|
|
output,
|
|
[(source, "message.txt")],
|
|
password_protected=True,
|
|
method="zip_standard",
|
|
)
|
|
self.assertEqual("Legacy ZipCrypto", evidence["format"])
|
|
self.assertEqual("govoplan-campaign.zipcrypto", evidence["implementation"])
|
|
self.assertNotIn("password", evidence)
|
|
self.assertEqual(64, len(str(evidence["archive_sha256"])))
|
|
self.assertEqual(64, len(str(evidence["members"][0]["sha256"])))
|
|
|
|
@unittest.skipIf(pyzipper is None, "pyzipper is not installed")
|
|
def test_aes_password_zip_keeps_aes_encryption(self) -> None:
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
root = Path(temp_dir)
|
|
source = root / "source.txt"
|
|
source.write_text("Hello AES ZIP", encoding="utf-8")
|
|
output = root / "aes.zip"
|
|
|
|
create_zip_archive(output, [(source, "message.txt")], "secret", "aes")
|
|
|
|
with zipfile.ZipFile(output) as archive:
|
|
info = archive.getinfo("message.txt")
|
|
self.assertEqual(info.compress_type, 99)
|
|
self.assertTrue(info.flag_bits & 0x1)
|
|
|
|
with pyzipper.AESZipFile(output) as archive:
|
|
archive.setpassword(b"secret")
|
|
self.assertEqual(archive.read("message.txt"), b"Hello AES ZIP")
|
|
|
|
def test_unprotected_zip_ignores_encryption_mode(self) -> None:
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
root = Path(temp_dir)
|
|
source = root / "source.txt"
|
|
source.write_text("Plain ZIP", encoding="utf-8")
|
|
output = root / "plain.zip"
|
|
|
|
create_zip_archive(output, [(source, "message.txt")], "", "zip_standard")
|
|
|
|
with zipfile.ZipFile(output) as archive:
|
|
info = archive.getinfo("message.txt")
|
|
self.assertEqual(info.compress_type, zipfile.ZIP_DEFLATED)
|
|
self.assertFalse(info.flag_bits & 0x1)
|
|
self.assertEqual(archive.read("message.txt"), b"Plain ZIP")
|
|
|
|
def test_unknown_password_method_fails_without_downgrade_or_output(self) -> None:
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
root = Path(temp_dir)
|
|
source = root / "source.txt"
|
|
source.write_text("No downgrade", encoding="utf-8")
|
|
output = root / "unknown.zip"
|
|
|
|
with self.assertRaisesRegex(ValueError, "Unsupported"):
|
|
create_zip_archive(output, [source], "secret", "unknown")
|
|
|
|
self.assertFalse(output.exists())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|