From 59041001356575183e3443b221b4162092b59259 Mon Sep 17 00:00:00 2001 From: stumpylog <797416+stumpylog@users.noreply.github.com> Date: Thu, 23 Jul 2026 15:04:55 -0700 Subject: [PATCH] Feature: importer rejects archives with unreadable compression --- .../management/commands/document_importer.py | 13 +++++++++ .../tests/test_management_importer.py | 29 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/documents/management/commands/document_importer.py b/src/documents/management/commands/document_importer.py index 0e54b0ced..ad9efcc86 100644 --- a/src/documents/management/commands/document_importer.py +++ b/src/documents/management/commands/document_importer.py @@ -32,6 +32,8 @@ from django.db.models.signals import post_save from filelock import FileLock from guardian.shortcuts import clear_ct_cache +from documents.export.compression import compress_type_readable +from documents.export.compression import unreadable_method_names from documents.file_handling import create_source_path_directory from documents.management.commands.base import PaperlessCommand from documents.management.commands.mixins import CryptMixin @@ -460,6 +462,17 @@ class Command(CryptMixin, PaperlessCommand): with tempfile.TemporaryDirectory() as tmp_dir: if is_zipfile(self.source): with ZipFile(self.source) as zf: + unsupported = { + info.compress_type + for info in zf.infolist() + if not compress_type_readable(info.compress_type) + } + if unsupported: + names = ", ".join(sorted(unreadable_method_names(unsupported))) + raise CommandError( + f"This archive uses compression this Python cannot " + f"read ({names}). zstd archives require Python 3.14+.", + ) zf.extractall(tmp_dir) self.source = Path(tmp_dir) self._run_import() diff --git a/src/documents/tests/test_management_importer.py b/src/documents/tests/test_management_importer.py index 33eec7267..243de9d64 100644 --- a/src/documents/tests/test_management_importer.py +++ b/src/documents/tests/test_management_importer.py @@ -525,6 +525,35 @@ class TestCommandImport( self.assertEqual(doc.tags.count(), 1) self.assertEqual(doc.tags.first().name, "batch-flush-tag") + def test_import_rejects_unreadable_compression(self) -> None: + """ + GIVEN: + - A zip archive with an entry whose compression this Python can't read + WHEN: + - Import is attempted + THEN: + - A CommandError naming the issue is raised, before extraction + """ + import zipfile + from unittest import mock + + archive = Path(self.dirs.scratch_dir) / "export.zip" + with zipfile.ZipFile(archive, "w") as zf: + zf.writestr("manifest.json", "[]") + + with mock.patch( + "documents.management.commands.document_importer.compress_type_readable", + return_value=False, + ): + with self.assertRaises(CommandError) as e: + call_command( + "document_importer", + str(archive), + "--no-progress-bar", + skip_checks=True, + ) + self.assertIn("compression", str(e.exception)) + @pytest.mark.management @pytest.mark.django_db