1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-11-20 13:50:12 +01:00

update: strip all metadata from PNGs

This strips metadata and optimizes the compression of all PNGs copied
from the app's source repo as well as all the icons extracted from the
APKs.  There have been exploits delivered via image metadata, and
F-Droid isn't using it all, so its best to just remove it.

This unfortunately uncompresses and recompresses the files.  Luckily,
that's a lossless procedure with PNGs, and we might end up with
smaller files.  The only tool I could find that strips without
changing the image data is exiftool, but that is written in Perl.
This commit is contained in:
Hans-Christoph Steiner 2017-12-13 11:51:34 +01:00
parent 67b9514c5a
commit 387eebc4d6

View File

@ -35,7 +35,7 @@ from argparse import ArgumentParser
import collections import collections
from binascii import hexlify from binascii import hexlify
from PIL import Image from PIL import Image, PngImagePlugin
import logging import logging
from . import _ from . import _
@ -84,6 +84,8 @@ GRAPHIC_NAMES = ('featureGraphic', 'icon', 'promoGraphic', 'tvBanner')
SCREENSHOT_DIRS = ('phoneScreenshots', 'sevenInchScreenshots', SCREENSHOT_DIRS = ('phoneScreenshots', 'sevenInchScreenshots',
'tenInchScreenshots', 'tvScreenshots', 'wearScreenshots') 'tenInchScreenshots', 'tvScreenshots', 'wearScreenshots')
BLANK_PNG_INFO = PngImagePlugin.PngInfo()
def dpi_to_px(density): def dpi_to_px(density):
return (int(density) * 48) / 160 return (int(density) * 48) / 160
@ -371,7 +373,8 @@ def resize_icon(iconpath, density):
im.thumbnail((size, size), Image.ANTIALIAS) im.thumbnail((size, size), Image.ANTIALIAS)
logging.debug("%s was too large at %s - new size is %s" % ( logging.debug("%s was too large at %s - new size is %s" % (
iconpath, oldsize, im.size)) iconpath, oldsize, im.size))
im.save(iconpath, "PNG") im.save(iconpath, "PNG", optimize=True,
pnginfo=BLANK_PNG_INFO, icc_profile=None)
except Exception as e: except Exception as e:
logging.error(_("Failed resizing {path}: {error}".format(path=iconpath, error=e))) logging.error(_("Failed resizing {path}: {error}".format(path=iconpath, error=e)))
@ -677,20 +680,28 @@ def _strip_and_copy_image(inpath, outpath):
Sadly, image metadata like EXIF can be used to exploit devices. Sadly, image metadata like EXIF can be used to exploit devices.
It is not used at all in the F-Droid ecosystem, so its much safer It is not used at all in the F-Droid ecosystem, so its much safer
just to remove it entirely. PNG does not have the same kind of just to remove it entirely.
issues.
""" """
if common.has_extension(inpath, 'png'): extension = common.get_extension(inpath)[1]
shutil.copy(inpath, outpath) if os.path.isdir(outpath):
else: outpath = os.path.join(outpath, os.path.basename(inpath))
with open(inpath) as fp: if extension == 'png':
with open(inpath, 'rb') as fp:
in_image = Image.open(fp)
in_image.save(outpath, "PNG", optimize=True,
pnginfo=BLANK_PNG_INFO, icc_profile=None)
elif extension == 'jpg' or extension == 'jpeg':
with open(inpath, 'rb') as fp:
in_image = Image.open(fp) in_image = Image.open(fp)
data = list(in_image.getdata()) data = list(in_image.getdata())
out_image = Image.new(in_image.mode, in_image.size) out_image = Image.new(in_image.mode, in_image.size)
out_image.putdata(data) out_image.putdata(data)
out_image.save(outpath, "JPEG", optimize=True) out_image.save(outpath, "JPEG", optimize=True)
else:
raise FDroidException(_('Unsupported file type "{extension}" for repo graphic')
.format(extension=extension))
def copy_triple_t_store_metadata(apps): def copy_triple_t_store_metadata(apps):
@ -1512,7 +1523,8 @@ def fill_missing_icon_densities(empty_densities, icon_filename, apk, repo_dir):
size = dpi_to_px(density) size = dpi_to_px(density)
im.thumbnail((size, size), Image.ANTIALIAS) im.thumbnail((size, size), Image.ANTIALIAS)
im.save(icon_path, "PNG") im.save(icon_path, "PNG", optimize=True,
pnginfo=BLANK_PNG_INFO, icc_profile=None)
empty_densities.remove(density) empty_densities.remove(density)
except Exception as e: except Exception as e:
logging.warning("Invalid image file at %s: %s", last_icon_path, e) logging.warning("Invalid image file at %s: %s", last_icon_path, e)