1
1
import logging
2
+ from pathlib import Path
2
3
from typing import Iterable
3
4
4
5
from django .core .files .uploadedfile import UploadedFile
@@ -56,29 +57,40 @@ def __init__(self, allowed_mime_types: Iterable[str] | None = None):
56
57
57
58
def __call__ (self , value : UploadedFile ) -> None :
58
59
head = value .read (2048 )
59
- ext = value .name .split ("." )[- 1 ]
60
- mime_type = magic .from_buffer (head , mime = True )
60
+ ext = Path (value .name or "" ).suffix [1 :]
61
+ detected_mime_type = magic .from_buffer (head , mime = True )
62
+ provided_mime_type = value .content_type or "application/octet-stream"
61
63
62
64
# gh #2520
63
65
# application/x-ole-storage on Arch with shared-mime-info 2.0+155+gf4e7cbc-1
64
- if mime_type in ["application/CDFV2" , "application/x-ole-storage" ]:
66
+ if detected_mime_type in ["application/CDFV2" , "application/x-ole-storage" ]:
65
67
whole_file = head + value .read ()
66
- mime_type = magic .from_buffer (whole_file , mime = True )
68
+ detected_mime_type = magic .from_buffer (whole_file , mime = True )
67
69
68
- if mime_type == "image/heif" :
69
- mime_type = "image/heic"
70
+ if detected_mime_type == "image/heif" :
71
+ detected_mime_type = "image/heic"
70
72
71
73
if not (
72
74
self .any_allowed
73
- or mimetype_allowed (mime_type , self ._regular_mimes , self ._wildcard_mimes )
75
+ or mimetype_allowed (
76
+ detected_mime_type , self ._regular_mimes , self ._wildcard_mimes
77
+ )
74
78
):
75
79
raise serializers .ValidationError (
76
80
_ ("The provided file is not a valid file type." )
77
81
)
78
82
83
+ if not ext :
84
+ raise serializers .ValidationError (
85
+ _ (
86
+ "Could not determine the file type. Please make sure the file name "
87
+ "has an extension."
88
+ )
89
+ )
90
+
79
91
# Contents is allowed. Do extension or submitted content_type agree?
80
- if value . content_type == "application/octet-stream" :
81
- m = magic .Magic (extension = True )
92
+ if provided_mime_type == "application/octet-stream" :
93
+ m = magic .Magic (extension = True ) # pyright: ignore[reportCallIssue]
82
94
extensions = m .from_buffer (head ).split ("/" )
83
95
# magic db doesn't know any more specific extension(s), so accept the
84
96
# file
@@ -101,27 +113,26 @@ def __call__(self, value: UploadedFile) -> None:
101
113
# If the file does not strictly follow the conventions of CSV (e.g. non-standard delimiters),
102
114
# may not be considered as a valid CSV.
103
115
elif (
104
- value . content_type == "text/csv"
105
- and mime_type == "text/plain"
116
+ provided_mime_type == "text/csv"
117
+ and detected_mime_type == "text/plain"
106
118
and ext == "csv"
107
119
):
108
120
return
109
- elif mime_type == "image/heic" and value . content_type in (
121
+ elif detected_mime_type == "image/heic" and provided_mime_type in (
110
122
"image/heic" ,
111
123
"image/heif" ,
112
124
):
113
125
return
114
-
115
126
# gh #4658
116
127
# Windows use application/x-zip-compressed as a mimetype for .zip files, which
117
128
# is deprecated but still we need to support it. Instead, the common case for
118
129
# zip files is application/zip or application/zip-compressed mimetype.
119
- elif mime_type == "application/zip" and value . content_type in (
130
+ elif detected_mime_type == "application/zip" and provided_mime_type in (
120
131
"application/zip-compressed" ,
121
132
"application/x-zip-compressed" ,
122
133
):
123
134
return
124
- elif mime_type != value . content_type :
135
+ elif provided_mime_type != detected_mime_type :
125
136
raise serializers .ValidationError (
126
137
_ ("The provided file is not a {file_type}." ).format (
127
138
filename = value .name , file_type = f".{ ext } "
0 commit comments