Skip to content

Commit e86458e

Browse files
committed
Check magic numbers for filetype
1 parent eca1ffc commit e86458e

File tree

8 files changed

+93
-33
lines changed

8 files changed

+93
-33
lines changed

testing/media/testfile/the_file.png

2.07 KB
Loading

validatedfile/models.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,32 @@
33
from django.template.defaultfilters import filesizeformat
44
from django.utils.translation import ugettext as _
55

6+
import magic
7+
68
class ValidatedFileField(models.FileField):
79
def __init__(self, *args, **kwargs):
8-
self.content_types = kwargs.pop("content_types")
9-
self.max_upload_size = kwargs.pop("max_upload_size")
10+
self.content_types = kwargs.pop("content_types", [])
11+
self.max_upload_size = kwargs.pop("max_upload_size", 0)
1012
super(ValidatedFileField, self).__init__(*args, **kwargs)
1113

1214
def clean(self, *args, **kwargs):
1315
data = super(ValidatedFileField, self).clean(*args, **kwargs)
1416

15-
file = data.file
16-
try:
17-
content_type = file.content_type
18-
if content_type in self.content_types:
19-
if file._size > self.max_upload_size:
20-
raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s') % (filesizeformat(self.max_upload_size), filesizeformat(file._size)))
21-
else:
17+
if self.content_types:
18+
file = data.file
19+
content_type_headers = getattr(file, 'content_type', '')
20+
21+
mg = magic.Magic(mime = True)
22+
content_type_magic = mg.from_buffer(file.read(1024))
23+
file.seek(0)
24+
25+
if not content_type_headers in self.content_types or not content_type_magic in self.content_types:
2226
raise forms.ValidationError(_('Filetype %s not supported.') % (file.content_type))
23-
except AttributeError:
24-
pass
27+
28+
if self.max_upload_size:
29+
if file._size > self.max_upload_size:
30+
raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s') %
31+
(filesizeformat(self.max_upload_size), filesizeformat(file._size)))
2532

2633
return data
34+

validatedfile/tests/__init__.py

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,14 @@
55

66
import os.path
77

8-
from models import TestModel
9-
from forms import TestModelForm
8+
from models import TestModel, TestModelNoValidate
9+
from forms import TestModelForm, TestModelNoValidateForm
1010

1111
class ValidatedFileFieldTest(TestCase):
1212

1313
SAMPLE_FILES_PATH = 'validatedfile/tests/sample_files'
1414

1515

16-
def _get_sample_file(self, filename):
17-
path = os.path.join(self.SAMPLE_FILES_PATH, filename)
18-
return open(path)
19-
20-
21-
def _check_file_url(self, filefield, filename):
22-
url = os.path.join(settings.MEDIA_URL, filefield.field.upload_to, filename)
23-
self.assertEqual(filefield.url, url)
24-
25-
26-
def _get_file_url(self, filename):
27-
return os.path.join(MEDIA_ROOT, prefix, filename)
28-
29-
3016
def test_create_empty_instance(self):
3117
instance = TestModel.objects.create()
3218
instance.save()
@@ -40,11 +26,12 @@ def test_create_instance_with_file(self):
4026

4127
self._check_file_url(instance.the_file, 'the_file.png')
4228

29+
from ipdb import set_trace; set_trace()
4330
instance.the_file.delete()
4431
instance.delete()
4532

4633

47-
def test_form(self):
34+
def test_form_ok(self):
4835
uploaded_file = SimpleUploadedFile(
4936
name = 'the_file.png',
5037
content = self._get_sample_file('image2k.png').read(),
@@ -60,26 +47,80 @@ def test_form(self):
6047
instance.delete()
6148

6249

50+
def test_form_invalid_size(self):
51+
uploaded_file = SimpleUploadedFile(
52+
name = 'the_file.png',
53+
content = self._get_sample_file('image15k.png').read(),
54+
content_type = 'image/png',
55+
)
56+
form = TestModelForm(data = {}, files = {'the_file': uploaded_file})
57+
self.assertFalse(form.is_valid())
58+
self.assertEqual(len(form.errors), 1)
59+
self.assertEqual(len(form.errors['the_file']), 1)
60+
61+
6362
def test_form_invalid_filetype(self):
6463
uploaded_file = SimpleUploadedFile(
6564
name = 'the_file.pdf',
66-
content = self._get_sample_file('document.pdf').read(),
67-
content_type = 'apllication/pdf',
65+
content = self._get_sample_file('document1k.pdf').read(),
66+
content_type = 'application/pdf',
6867
)
6968
form = TestModelForm(data = {}, files = {'the_file': uploaded_file})
7069
self.assertFalse(form.is_valid())
7170
self.assertEqual(len(form.errors), 1)
7271
self.assertEqual(len(form.errors['the_file']), 1)
7372

7473

75-
def test_form_invalid_size(self):
74+
def test_form_invalid_filetype_and_size(self):
7675
uploaded_file = SimpleUploadedFile(
7776
name = 'the_file.pdf',
78-
content = self._get_sample_file('image15k.png').read(),
77+
content = self._get_sample_file('document15k.pdf').read(),
78+
content_type = 'application/pdf',
79+
)
80+
form = TestModelForm(data = {}, files = {'the_file': uploaded_file})
81+
self.assertFalse(form.is_valid())
82+
self.assertEqual(len(form.errors), 1)
83+
self.assertEqual(len(form.errors['the_file']), 1)
84+
85+
86+
def test_form_fake_filetype(self):
87+
uploaded_file = SimpleUploadedFile(
88+
name = 'the_file.png',
89+
content = self._get_sample_file('document1k.pdf').read(),
7990
content_type = 'image/png',
8091
)
8192
form = TestModelForm(data = {}, files = {'the_file': uploaded_file})
8293
self.assertFalse(form.is_valid())
8394
self.assertEqual(len(form.errors), 1)
8495
self.assertEqual(len(form.errors['the_file']), 1)
8596

97+
98+
def test_form_no_validate(self):
99+
uploaded_file = SimpleUploadedFile(
100+
name = 'the_file.pdf',
101+
content = self._get_sample_file('document15k.pdf').read(),
102+
content_type = 'application/pdf',
103+
)
104+
form = TestModelNoValidateForm(data = {}, files = {'the_file': uploaded_file})
105+
self.assertTrue(form.is_valid())
106+
instance = form.save()
107+
108+
self._check_file_url(instance.the_file, 'the_file.pdf')
109+
110+
instance.the_file.delete()
111+
instance.delete()
112+
113+
114+
def _get_sample_file(self, filename):
115+
path = os.path.join(self.SAMPLE_FILES_PATH, filename)
116+
return open(path)
117+
118+
119+
def _check_file_url(self, filefield, filename):
120+
url = os.path.join(settings.MEDIA_URL, filefield.field.upload_to, filename)
121+
self.assertEqual(filefield.url, url)
122+
123+
124+
def _get_file_url(self, filename):
125+
return os.path.join(MEDIA_ROOT, prefix, filename)
126+

validatedfile/tests/forms.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
from django import forms
2-
from models import TestModel
2+
from models import TestModel, TestModelNoValidate
33

44
class TestModelForm(forms.ModelForm):
55

66
class Meta:
77
model = TestModel
88

9+
class TestModelNoValidateForm(forms.ModelForm):
10+
11+
class Meta:
12+
model = TestModelNoValidate
13+

validatedfile/tests/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@ class TestModel(models.Model):
1010
content_types = ['image/png'],
1111
max_upload_size = 10240)
1212

13+
class TestModelNoValidate(models.Model):
14+
the_file = ValidatedFileField(
15+
null = True,
16+
blank = True,
17+
upload_to = 'testfile')
18+
1.13 KB
Binary file not shown.

0 commit comments

Comments
 (0)