Merge pull request #2410 from wiredfool/crop_decompression

Add decompression bomb check to Image.crop
This commit is contained in:
wiredfool 2017-06-21 11:00:03 +01:00 committed by GitHub
commit a4dafe78df
3 changed files with 38 additions and 10 deletions

View File

@ -257,7 +257,7 @@ class GifImageFile(ImageFile.ImageFile):
# only dispose the extent in this frame # only dispose the extent in this frame
if self.dispose: if self.dispose:
self.dispose = self.dispose.crop(self.dispose_extent) self.dispose = self._crop(self.dispose, self.dispose_extent)
except (AttributeError, KeyError): except (AttributeError, KeyError):
pass pass
@ -280,7 +280,7 @@ class GifImageFile(ImageFile.ImageFile):
if self._prev_im and self.disposal_method == 1: if self._prev_im and self.disposal_method == 1:
# we do this by pasting the updated area onto the previous # we do this by pasting the updated area onto the previous
# frame which we then use as the current image content # frame which we then use as the current image content
updated = self.im.crop(self.dispose_extent) updated = self._crop(self.im, self.dispose_extent)
self._prev_im.paste(updated, self.dispose_extent, self._prev_im.paste(updated, self.dispose_extent,
updated.convert('RGBA')) updated.convert('RGBA'))
self.im = self._prev_im self.im = self._prev_im

View File

@ -1042,18 +1042,31 @@ class Image(object):
if box is None: if box is None:
return self.copy() return self.copy()
x0, y0, x1, y1 = map(int, map(round, box)) return self._new(self._crop(self.im, box))
if x0 == 0 and y0 == 0 and (x1, y1) == self.size: def _crop(self, im, box):
return self.copy() """
Returns a rectangular region from the core image object im.
This is equivalent to calling im.crop((x0, y0, x1, y1)), but
includes additional sanity checks.
:param im: a core image object
:param box: The crop rectangle, as a (left, upper, right, lower)-tuple.
:returns: A core image object.
"""
x0, y0, x1, y1 = map(int, map(round, box))
if x1 < x0: if x1 < x0:
x1 = x0 x1 = x0
if y1 < y0: if y1 < y0:
y1 = y0 y1 = y0
return self._new(self.im.crop((x0, y0, x1, y1))) _decompression_bomb_check((x1, y1))
return im.crop((x0, y0, x1, y1))
def draft(self, mode, size): def draft(self, mode, size):
""" """
Configures the image file loader so it returns a version of the Configures the image file loader so it returns a version of the

View File

@ -1,4 +1,4 @@
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase, hopper
from PIL import Image from PIL import Image
@ -35,9 +35,24 @@ class TestDecompressionBomb(PillowTestCase):
self.assertEqual(Image.MAX_IMAGE_PIXELS, 10) self.assertEqual(Image.MAX_IMAGE_PIXELS, 10)
# Act / Assert # Act / Assert
self.assert_warning( self.assert_warning(Image.DecompressionBombWarning,
Image.DecompressionBombWarning, lambda: Image.open(TEST_FILE))
lambda: Image.open(TEST_FILE))
class TestDecompressionCrop(PillowTestCase):
def setUp(self):
self.src = hopper()
Image.MAX_IMAGE_PIXELS = self.src.height * self.src.width
def tearDown(self):
Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT
def testEnlargeCrop(self):
# Crops can extend the extents, therefore we should have the
# same decompression bomb warnings on them.
box = (0, 0, self.src.width * 2, self.src.height * 2)
self.assert_warning(Image.DecompressionBombWarning,
lambda: self.src.crop(box))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()