Verified Commit a7d77565 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Fix #374: Strip EXIF metadata from uploaded avatars to avoid leaking private data

parent ddeb1a66
import collections
import io
import PIL
import os
from rest_framework import serializers
from django.core.exceptions import ObjectDoesNotExist
from django.core.files.uploadedfile import SimpleUploadedFile
from django.utils.encoding import smart_text
from django.utils.translation import ugettext_lazy as _
......@@ -190,3 +194,25 @@ def track_fields_for_update(*fields):
return serializer_class
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 funkwhale_api.activity import serializers as activity_serializers
from funkwhale_api.common import serializers as common_serializers
from . import models
......@@ -66,7 +66,13 @@ class UserActivitySerializer(activity_serializers.ModelSerializer):
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):
......
import os
import PIL
from django.core.files.uploadedfile import SimpleUploadedFile
import django_filters
from funkwhale_api.common import serializers
......@@ -163,3 +168,17 @@ def test_track_fields_for_update(mocker):
{"field1": "value1", "field2": "value2"},
{"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
Strip EXIF metadata from uploaded avatars to avoid leaking private data (#374)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment