mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-11 15:54:45 +03:00
Put CVE fix in for CVE-2022-22817 Restrict builtins for ImageMath.eval()
Put in fixes from CVE Update release documentation Ensure all tests pass as before
This commit is contained in:
parent
538ac8d360
commit
1184cbf916
|
@ -1,4 +1,5 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageMath
|
from PIL import Image, ImageMath
|
||||||
|
|
||||||
|
@ -56,6 +57,10 @@ class TestImageMath(PillowTestCase):
|
||||||
pixel(ImageMath.eval("float(B)**33", images)), "F 8589934592.0"
|
pixel(ImageMath.eval("float(B)**33", images)), "F 8589934592.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_prevent_exec(self):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
ImageMath.eval("exec('pass')")
|
||||||
|
|
||||||
def test_logical(self):
|
def test_logical(self):
|
||||||
self.assertEqual(pixel(ImageMath.eval("not A", images)), 0)
|
self.assertEqual(pixel(ImageMath.eval("not A", images)), 0)
|
||||||
self.assertEqual(pixel(ImageMath.eval("A and B", images)), "L 2")
|
self.assertEqual(pixel(ImageMath.eval("A and B", images)), "L 2")
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
from PIL import Image, ImageShow
|
from PIL import Image, ImageShow
|
||||||
|
from tempfile import mkdtemp
|
||||||
|
from os import rmdir
|
||||||
|
from os.path import join
|
||||||
|
from shutil import rmtree
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, on_ci, unittest
|
from .helper import PillowTestCase, hopper, on_ci, unittest
|
||||||
|
|
||||||
|
@ -50,3 +54,11 @@ class TestImageShow(PillowTestCase):
|
||||||
def test_viewers(self):
|
def test_viewers(self):
|
||||||
for viewer in ImageShow._viewers:
|
for viewer in ImageShow._viewers:
|
||||||
viewer.get_command("test.jpg")
|
viewer.get_command("test.jpg")
|
||||||
|
|
||||||
|
def test_file_deprecated(self):
|
||||||
|
tmp_path = mkdtemp()
|
||||||
|
f = join(tmp_path, "temp.jpg")
|
||||||
|
for viewer in ImageShow._viewers:
|
||||||
|
hopper().save(f)
|
||||||
|
viewer.show_file(file=f)
|
||||||
|
# viewer.show_file()
|
||||||
|
|
21
docs/releasenotes/6.2.2.3.rst
Normal file
21
docs/releasenotes/6.2.2.3.rst
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
6.2.2.3
|
||||||
|
-------
|
||||||
|
|
||||||
|
Security
|
||||||
|
========
|
||||||
|
|
||||||
|
This release addresses several critical CVEs.
|
||||||
|
|
||||||
|
restrict builtins available to ImageMath.eval
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
CVE-2022-22817 Restrict builtins for ImageMath.eval()
|
||||||
|
|
||||||
|
To limit :py:class:`PIL.ImageMath` to working with images, Pillow will now restrict the
|
||||||
|
builtins available to :py:meth:`PIL.ImageMath.eval`. This will help prevent problems
|
||||||
|
arising if users evaluate arbitrary expressions, such as
|
||||||
|
``ImageMath.eval("exec(exit())")``.
|
||||||
|
|
||||||
|
CVE-2022-24303 Pillow before 9.0.1 allows attackers to delete files because spaces in temporary pathnames are mishandled.
|
||||||
|
|
||||||
|
A bunch of changes related to temporary files and race conditions are fixed
|
|
@ -264,7 +264,13 @@ def eval(expression, _dict={}, **kw):
|
||||||
if hasattr(v, "im"):
|
if hasattr(v, "im"):
|
||||||
args[k] = _Operand(v)
|
args[k] = _Operand(v)
|
||||||
|
|
||||||
out = builtins.eval(expression, args)
|
# out = builtins.eval(expression, args)
|
||||||
|
code = compile(expression, "<string>", "eval")
|
||||||
|
for name in code.co_names:
|
||||||
|
if name not in args and name != "abs":
|
||||||
|
raise ValueError(f"'{name}' not allowed")
|
||||||
|
|
||||||
|
out = builtins.eval(expression, {"__builtins": {"abs": abs}}, args)
|
||||||
try:
|
try:
|
||||||
return out.im
|
return out.im
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
|
@ -17,7 +17,6 @@ from __future__ import print_function
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
@ -98,6 +97,15 @@ class Viewer(object):
|
||||||
os.system(self.get_command(file, **options))
|
os.system(self.get_command(file, **options))
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
def _remove_path_after_delay(self, path):
|
||||||
|
subprocess.Popen(
|
||||||
|
[
|
||||||
|
sys.executable,
|
||||||
|
"-c",
|
||||||
|
"import os, sys, time; time.sleep(20); os.remove(sys.argv[1])",
|
||||||
|
path,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -136,16 +144,9 @@ elif sys.platform == "darwin":
|
||||||
|
|
||||||
def show_file(self, file, **options):
|
def show_file(self, file, **options):
|
||||||
"""Display given file"""
|
"""Display given file"""
|
||||||
fd, path = tempfile.mkstemp()
|
|
||||||
with os.fdopen(fd, "w") as f:
|
subprocess.call(["open", "-a", "Preview.app", file])
|
||||||
f.write(file)
|
self._remove_path_after_delay(file)
|
||||||
with open(path, "r") as f:
|
|
||||||
subprocess.Popen(
|
|
||||||
["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"],
|
|
||||||
shell=True,
|
|
||||||
stdin=f,
|
|
||||||
)
|
|
||||||
os.remove(path)
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
register(MacViewer)
|
register(MacViewer)
|
||||||
|
@ -168,49 +169,49 @@ else:
|
||||||
format = "PNG"
|
format = "PNG"
|
||||||
options = {"compress_level": 1}
|
options = {"compress_level": 1}
|
||||||
|
|
||||||
def get_command(self, file, **options):
|
def get_command(self,file,**options):
|
||||||
command = self.get_command_ex(file, **options)[0]
|
return " ".join(self.get_command_ex(file,options=options))
|
||||||
return "(%s %s; rm -f %s)&" % (command, quote(file), quote(file))
|
|
||||||
|
def get_command_ex(self, file, **options):
|
||||||
|
return ["display",file]
|
||||||
|
|
||||||
def show_file(self, file, **options):
|
def show_file(self, file, **options):
|
||||||
"""Display given file"""
|
"""
|
||||||
fd, path = tempfile.mkstemp()
|
Display given file.
|
||||||
with os.fdopen(fd, "w") as f:
|
"""
|
||||||
f.write(file)
|
args = self.get_command_ex(file,**options)
|
||||||
with open(path, "r") as f:
|
subprocess.Popen(args)
|
||||||
command = self.get_command_ex(file, **options)[0]
|
|
||||||
subprocess.Popen(
|
self._remove_path_after_delay(file)
|
||||||
["im=$(cat);" + command + " $im; rm -f $im"], shell=True, stdin=f
|
|
||||||
)
|
|
||||||
os.remove(path)
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# implementations
|
# implementations
|
||||||
|
|
||||||
class DisplayViewer(UnixViewer):
|
class DisplayViewer(UnixViewer):
|
||||||
def get_command_ex(self, file, **options):
|
def get_command_ex(self, file, **options):
|
||||||
command = executable = "display"
|
return ["display", file]
|
||||||
return command, executable
|
|
||||||
|
|
||||||
if which("display"):
|
if which("display"):
|
||||||
register(DisplayViewer)
|
register(DisplayViewer)
|
||||||
|
|
||||||
class EogViewer(UnixViewer):
|
class EogViewer(UnixViewer):
|
||||||
def get_command_ex(self, file, **options):
|
def get_command_ex(self, file, **options):
|
||||||
command = executable = "eog"
|
return ["eog", "-n", file]
|
||||||
return command, executable
|
|
||||||
|
|
||||||
if which("eog"):
|
if which("eog"):
|
||||||
register(EogViewer)
|
register(EogViewer)
|
||||||
|
|
||||||
class XVViewer(UnixViewer):
|
class XVViewer(UnixViewer):
|
||||||
def get_command_ex(self, file, title=None, **options):
|
def get_command_ex(self, file, **options):
|
||||||
# note: xv is pretty outdated. most modern systems have
|
# note: xv is pretty outdated. most modern systems have
|
||||||
# imagemagick's display command instead.
|
# imagemagick's display command instead.
|
||||||
command = executable = "xv"
|
args = ["xv"]
|
||||||
if title:
|
if options.get("title") is not None:
|
||||||
command += " -name %s" % quote(title)
|
args.append("-name")
|
||||||
return command, executable
|
args.append(options.get("title"))
|
||||||
|
args.append(file)
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
if which("xv"):
|
if which("xv"):
|
||||||
register(XVViewer)
|
register(XVViewer)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user