Fix #374: Strip EXIF metadata from uploaded avatars to avoid leaking private data
This commit is contained in:
parent
ddeb1a66fc
commit
a7d7756545
|
@ -1,8 +1,12 @@
|
||||||
import collections
|
import collections
|
||||||
|
import io
|
||||||
|
import PIL
|
||||||
|
import os
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
from django.utils.encoding import smart_text
|
from django.utils.encoding import smart_text
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -190,3 +194,25 @@ def track_fields_for_update(*fields):
|
||||||
return serializer_class
|
return serializer_class
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
class StripExifImageField(serializers.ImageField):
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
file_obj = super().to_internal_value(data)
|
||||||
|
|
||||||
|
image = PIL.Image.open(file_obj)
|
||||||
|
data = list(image.getdata())
|
||||||
|
image_without_exif = PIL.Image.new(image.mode, image.size)
|
||||||
|
image_without_exif.putdata(data)
|
||||||
|
|
||||||
|
with io.BytesIO() as output:
|
||||||
|
image_without_exif.save(
|
||||||
|
output,
|
||||||
|
format=PIL.Image.EXTENSION[os.path.splitext(file_obj.name)[-1]],
|
||||||
|
quality=100,
|
||||||
|
)
|
||||||
|
content = output.getvalue()
|
||||||
|
|
||||||
|
return SimpleUploadedFile(
|
||||||
|
file_obj.name, content, content_type=file_obj.content_type
|
||||||
|
)
|
||||||
|
|
|
@ -11,7 +11,7 @@ from rest_framework import serializers
|
||||||
from versatileimagefield.serializers import VersatileImageFieldSerializer
|
from versatileimagefield.serializers import VersatileImageFieldSerializer
|
||||||
|
|
||||||
from funkwhale_api.activity import serializers as activity_serializers
|
from funkwhale_api.activity import serializers as activity_serializers
|
||||||
|
from funkwhale_api.common import serializers as common_serializers
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,7 +66,13 @@ class UserActivitySerializer(activity_serializers.ModelSerializer):
|
||||||
return "Person"
|
return "Person"
|
||||||
|
|
||||||
|
|
||||||
avatar_field = VersatileImageFieldSerializer(allow_null=True, sizes="square")
|
class AvatarField(
|
||||||
|
common_serializers.StripExifImageField, VersatileImageFieldSerializer
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
avatar_field = AvatarField(allow_null=True, sizes="square")
|
||||||
|
|
||||||
|
|
||||||
class UserBasicSerializer(serializers.ModelSerializer):
|
class UserBasicSerializer(serializers.ModelSerializer):
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
|
@ -1,3 +1,8 @@
|
||||||
|
import os
|
||||||
|
import PIL
|
||||||
|
|
||||||
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
|
|
||||||
import django_filters
|
import django_filters
|
||||||
|
|
||||||
from funkwhale_api.common import serializers
|
from funkwhale_api.common import serializers
|
||||||
|
@ -163,3 +168,17 @@ def test_track_fields_for_update(mocker):
|
||||||
{"field1": "value1", "field2": "value2"},
|
{"field1": "value1", "field2": "value2"},
|
||||||
{"field1": "newvalue1", "field2": "newvalue2"},
|
{"field1": "newvalue1", "field2": "newvalue2"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_strip_exif_field():
|
||||||
|
source_path = os.path.join(os.path.dirname(__file__), "exif.jpg")
|
||||||
|
source = PIL.Image.open(source_path)
|
||||||
|
|
||||||
|
assert bool(source._getexif())
|
||||||
|
|
||||||
|
with open(source_path, "rb") as f:
|
||||||
|
uploaded = SimpleUploadedFile("source.jpg", f.read(), content_type="image/jpeg")
|
||||||
|
field = serializers.StripExifImageField()
|
||||||
|
|
||||||
|
cleaned = PIL.Image.open(field.to_internal_value(uploaded))
|
||||||
|
assert cleaned._getexif() is None
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Strip EXIF metadata from uploaded avatars to avoid leaking private data (#374)
|
Loading…
Reference in New Issue