Commit 387eebc4 authored by Hans-Christoph Steiner's avatar Hans-Christoph Steiner
Browse files

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.
parent 67b9514c
......@@ -35,7 +35,7 @@ from argparse import ArgumentParser
import collections
from binascii import hexlify
from PIL import Image
from PIL import Image, PngImagePlugin
import logging
from . import _
......@@ -84,6 +84,8 @@ GRAPHIC_NAMES = ('featureGraphic', 'icon', 'promoGraphic', 'tvBanner')
SCREENSHOT_DIRS = ('phoneScreenshots', 'sevenInchScreenshots',
'tenInchScreenshots', 'tvScreenshots', 'wearScreenshots')
BLANK_PNG_INFO = PngImagePlugin.PngInfo()
def dpi_to_px(density):
return (int(density) * 48) / 160
......@@ -371,7 +373,8 @@ def resize_icon(iconpath, density):
im.thumbnail((size, size), Image.ANTIALIAS)
logging.debug("%s was too large at %s - new size is %s" % (
iconpath, oldsize, im.size)), "PNG"), "PNG", optimize=True,
pnginfo=BLANK_PNG_INFO, icc_profile=None)
except Exception as 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.
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.
if common.has_extension(inpath, 'png'):
shutil.copy(inpath, outpath)
with open(inpath) as fp:
extension = common.get_extension(inpath)[1]
if os.path.isdir(outpath):
outpath = os.path.join(outpath, os.path.basename(inpath))
if extension == 'png':
with open(inpath, 'rb') as fp:
in_image =, "PNG", optimize=True,
pnginfo=BLANK_PNG_INFO, icc_profile=None)
elif extension == 'jpg' or extension == 'jpeg':
with open(inpath, 'rb') as fp:
in_image =
data = list(in_image.getdata())
out_image =, in_image.size)
out_image.putdata(data), "JPEG", optimize=True)
raise FDroidException(_('Unsupported file type "{extension}" for repo graphic')
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)
im.thumbnail((size, size), Image.ANTIALIAS), "PNG"), "PNG", optimize=True,
pnginfo=BLANK_PNG_INFO, icc_profile=None)
except Exception as e:
logging.warning("Invalid image file at %s: %s", last_icon_path, e)
Markdown is supported
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