(back) allow to disable checking unsafe mimetype on attachment upload

We added the possibility to scan all uploaded files with an anti malware
solution. Depending the backend used, we want to give the possibility to
check the file mimtype to determine if this one is tagged as unsafe or
not. To this you can set the environment variable
DOCUMENT_ATTACHMENT_CHECK_UNSAFE_MIME_TYPES_ENABLED to False. The
default value is True.
This commit is contained in:
Manuel Raynaud
2025-06-27 17:31:15 +02:00
committed by GitHub
parent 95a55e7805
commit 45bbffdf9f
5 changed files with 165 additions and 105 deletions

View File

@@ -15,6 +15,7 @@ and this project adheres to
- 📝(project) add system-requirement doc #1066 - 📝(project) add system-requirement doc #1066
- 🔧(front) configure x-frame-options to DENY in nginx conf #1084 - 🔧(front) configure x-frame-options to DENY in nginx conf #1084
- (doc) add documentation to install with compose #855 - (doc) add documentation to install with compose #855
- ✨(backend) allow to disable checking unsafe mimetype on attachment upload
### Changed ### Changed

View File

@@ -58,6 +58,7 @@ These are the environment variables you can set for the `impress-backend` contai
| DJANGO_EMAIL_USE_TLS | Use tls for email host connection | false | | DJANGO_EMAIL_USE_TLS | Use tls for email host connection | false |
| DJANGO_SECRET_KEY | Secret key | | | DJANGO_SECRET_KEY | Secret key | |
| DJANGO_SERVER_TO_SERVER_API_TOKENS | | [] | | DJANGO_SERVER_TO_SERVER_API_TOKENS | | [] |
| DOCUMENT_ATTACHMENT_CHECK_UNSAFE_MIME_TYPES_ENABLED | Check mime type extension to determine if a file is unsafe or not. Can be disable if an antivirus is used in the MALWARE_DETECTION_BACKEND | True |
| DOCUMENT_IMAGE_MAX_SIZE | Maximum size of document in bytes | 10485760 | | DOCUMENT_IMAGE_MAX_SIZE | Maximum size of document in bytes | 10485760 |
| FRONTEND_CSS_URL | To add a external css file to the app | | | FRONTEND_CSS_URL | To add a external css file to the app | |
| FRONTEND_HOMEPAGE_FEATURE_ENABLED | Frontend feature flag to display the homepage | false | | FRONTEND_HOMEPAGE_FEATURE_ENABLED | Frontend feature flag to display the homepage | false |

View File

@@ -517,7 +517,8 @@ class FileUploadSerializer(serializers.Serializer):
mime = magic.Magic(mime=True) mime = magic.Magic(mime=True)
magic_mime_type = mime.from_buffer(file.read(1024)) magic_mime_type = mime.from_buffer(file.read(1024))
file.seek(0) # Reset file pointer to the beginning after reading file.seek(0) # Reset file pointer to the beginning after reading
self.context["is_unsafe"] = False
if settings.DOCUMENT_ATTACHMENT_CHECK_UNSAFE_MIME_TYPES_ENABLED:
self.context["is_unsafe"] = ( self.context["is_unsafe"] = (
magic_mime_type in settings.DOCUMENT_UNSAFE_MIME_TYPES magic_mime_type in settings.DOCUMENT_UNSAFE_MIME_TYPES
) )

View File

@@ -439,3 +439,56 @@ def test_api_documents_attachment_upload_unsafe():
"application/octet-stream", "application/octet-stream",
] ]
assert file_head["ContentDisposition"] == 'attachment; filename="script.exe"' assert file_head["ContentDisposition"] == 'attachment; filename="script.exe"'
def test_api_documents_attachment_upload_unsafe_mime_types_disabled(settings):
"""A file with an unsafe mime type but checking disabled should not be tagged as unsafe."""
settings.DOCUMENT_ATTACHMENT_CHECK_UNSAFE_MIME_TYPES_ENABLED = False
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
document = factories.DocumentFactory(users=[(user, "owner")])
url = f"/api/v1.0/documents/{document.id!s}/attachment-upload/"
file = SimpleUploadedFile(
name="script.exe", content=b"\x4d\x5a\x90\x00\x03\x00\x00\x00"
)
with mock.patch.object(malware_detection, "analyse_file") as mock_analyse_file:
response = client.post(url, {"file": file}, format="multipart")
assert response.status_code == 201
pattern = re.compile(rf"^{document.id!s}/attachments/(.*)\.exe")
url_parsed = urlparse(response.json()["file"])
assert url_parsed.path == f"/api/v1.0/documents/{document.id!s}/media-check/"
query = parse_qs(url_parsed.query)
assert query["key"][0] is not None
file_path = query["key"][0]
match = pattern.search(file_path)
file_id = match.group(1)
document.refresh_from_db()
assert document.attachments == [f"{document.id!s}/attachments/{file_id!s}.exe"]
assert "-unsafe" not in file_id
# Validate that file_id is a valid UUID
uuid.UUID(file_id)
key = file_path.replace("/media/", "")
mock_analyse_file.assert_called_once_with(key, document_id=document.id)
# Now, check the metadata of the uploaded file
file_head = default_storage.connection.meta.client.head_object(
Bucket=default_storage.bucket_name, Key=key
)
assert file_head["Metadata"] == {
"owner": str(user.id),
"status": "processing",
}
# Depending the libmagic version, the content type may change.
assert file_head["ContentType"] in [
"application/x-dosexec",
"application/octet-stream",
]
assert file_head["ContentDisposition"] == 'attachment; filename="script.exe"'

View File

@@ -212,7 +212,11 @@ class Base(Configuration):
"application/x-msdownload", "application/x-msdownload",
"application/xml", "application/xml",
] ]
DOCUMENT_ATTACHMENT_CHECK_UNSAFE_MIME_TYPES_ENABLED = values.BooleanValue(
True,
environ_name="DOCUMENT_ATTACHMENT_CHECK_UNSAFE_MIME_TYPES_ENABLED",
environ_prefix=None,
)
# Document versions # Document versions
DOCUMENT_VERSIONS_PAGE_SIZE = 50 DOCUMENT_VERSIONS_PAGE_SIZE = 50