Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6deac9e3a2 | ||
|
|
c04d812b90 | ||
|
|
4fabec3619 | ||
|
|
02affaa491 | ||
|
|
ca0b585218 | ||
|
|
427221ef5f | ||
|
|
c930be0758 | ||
|
|
75b69dd239 | ||
|
|
cd938a7744 | ||
|
|
2e9c461ca4 |
13
CHANGES.rst
13
CHANGES.rst
@ -2,16 +2,25 @@
|
||||
Changelog (Pillow)
|
||||
==================
|
||||
|
||||
9.0.1 (2022-02-03)
|
||||
------------------
|
||||
|
||||
- In show_file, use os.remove to remove temporary images. CVE-2022-24303 #6010
|
||||
[radarhere, hugovk]
|
||||
|
||||
- Restrict builtins within lambdas for ImageMath.eval. CVE-2022-22817 #6009
|
||||
[radarhere]
|
||||
|
||||
9.0.0 (2022-01-02)
|
||||
------------------
|
||||
|
||||
- Restrict builtins for ImageMath.eval(). CVE TBD #5923
|
||||
- Restrict builtins for ImageMath.eval(). CVE-2022-22817 #5923
|
||||
[radarhere]
|
||||
|
||||
- Ensure JpegImagePlugin stops at the end of a truncated file #5921
|
||||
[radarhere]
|
||||
|
||||
- Fixed ImagePath.Path array handling. CVEs TBD #5920
|
||||
- Fixed ImagePath.Path array handling. CVE-2022-22815, CVE-2022-22816 #5920
|
||||
[radarhere]
|
||||
|
||||
- Remove consecutive duplicate tiles that only differ by their offset #5919
|
||||
|
||||
@ -52,9 +52,17 @@ def test_ops():
|
||||
assert pixel(ImageMath.eval("float(B)**33", images)) == "F 8589934592.0"
|
||||
|
||||
|
||||
def test_prevent_exec():
|
||||
@pytest.mark.parametrize(
|
||||
"expression",
|
||||
(
|
||||
"exec('pass')",
|
||||
"(lambda: exec('pass'))()",
|
||||
"(lambda: (lambda: exec('pass'))())()",
|
||||
),
|
||||
)
|
||||
def test_prevent_exec(expression):
|
||||
with pytest.raises(ValueError):
|
||||
ImageMath.eval("exec('pass')")
|
||||
ImageMath.eval(expression)
|
||||
|
||||
|
||||
def test_logical():
|
||||
|
||||
@ -119,15 +119,16 @@ Google's `OSS-Fuzz`_ project for finding this issue.
|
||||
Restrict builtins available to 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 TBD
|
||||
:cve:`CVE-2022-22817`: 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())")``.
|
||||
|
||||
Fixed ImagePath.Path array handling
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
CWE-126 and CWE-665 were found when initializing ``ImagePath.Path``. CVEs TBD
|
||||
:cve:`CVE-2022-22815` (:cwe:`CWE-126`) and :cve:`CVE-2022-22816` (:cwe:`CWE-665`) were
|
||||
found when initializing ``ImagePath.Path``.
|
||||
|
||||
.. _OSS-Fuzz: https://github.com/google/oss-fuzz
|
||||
|
||||
|
||||
23
docs/releasenotes/9.0.1.rst
Normal file
23
docs/releasenotes/9.0.1.rst
Normal file
@ -0,0 +1,23 @@
|
||||
9.0.1
|
||||
-----
|
||||
|
||||
Security
|
||||
========
|
||||
|
||||
This release addresses several security problems.
|
||||
|
||||
:cve:`CVE-2022-24303`: If the path to the temporary directory on Linux or macOS
|
||||
contained a space, this would break removal of the temporary image file after
|
||||
``im.show()`` (and related actions), and potentially remove an unrelated file. This
|
||||
been present since PIL.
|
||||
|
||||
:cve:`CVE-2022-22817`: While Pillow 9.0 restricted top-level builtins available to
|
||||
:py:meth:`PIL.ImageMath.eval`, it did not prevent builtins available to lambda
|
||||
expressions. These are now also restricted.
|
||||
|
||||
Other Changes
|
||||
=============
|
||||
|
||||
Pillow 9.0 added support for ``xdg-open`` as an image viewer, but there have been
|
||||
reports that the temporary image file was removed too quickly to be loaded into the
|
||||
final application. A delay has been added.
|
||||
@ -14,6 +14,7 @@ expected to be backported to earlier versions.
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
9.0.1
|
||||
9.0.0
|
||||
8.4.0
|
||||
8.3.2
|
||||
|
||||
@ -12,7 +12,7 @@ pytest-cov
|
||||
pytest-timeout
|
||||
sphinx>=2.4
|
||||
sphinx-copybutton
|
||||
sphinx-issues
|
||||
sphinx-issues>=3.0.1
|
||||
sphinx-removed-in
|
||||
sphinx-rtd-theme>=1.0
|
||||
sphinxext-opengraph
|
||||
|
||||
@ -246,11 +246,18 @@ def eval(expression, _dict={}, **kw):
|
||||
if hasattr(v, "im"):
|
||||
args[k] = _Operand(v)
|
||||
|
||||
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")
|
||||
compiled_code = compile(expression, "<string>", "eval")
|
||||
|
||||
def scan(code):
|
||||
for const in code.co_consts:
|
||||
if type(const) == type(compiled_code):
|
||||
scan(const)
|
||||
|
||||
for name in code.co_names:
|
||||
if name not in args and name != "abs":
|
||||
raise ValueError(f"'{name}' not allowed")
|
||||
|
||||
scan(compiled_code)
|
||||
out = builtins.eval(expression, {"__builtins": {"abs": abs}}, args)
|
||||
try:
|
||||
return out.im
|
||||
|
||||
@ -15,7 +15,6 @@ import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from shlex import quote
|
||||
|
||||
from PIL import Image
|
||||
@ -106,10 +105,20 @@ class Viewer:
|
||||
return self.show_file(self.save_image(image), **options)
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display the given file."""
|
||||
"""Display given file"""
|
||||
os.system(self.get_command(file, **options))
|
||||
return 1
|
||||
|
||||
def _remove_file_after_delay(self, file):
|
||||
subprocess.Popen(
|
||||
[
|
||||
sys.executable,
|
||||
"-c",
|
||||
"import os, sys, time; time.sleep(20); os.remove(sys.argv[1])",
|
||||
file,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
@ -147,16 +156,8 @@ class MacViewer(Viewer):
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
fd, path = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "w") as f:
|
||||
f.write(file)
|
||||
with open(path) as f:
|
||||
subprocess.Popen(
|
||||
["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"],
|
||||
shell=True,
|
||||
stdin=f,
|
||||
)
|
||||
os.remove(path)
|
||||
subprocess.call(["open", "-a", "Preview.app", file])
|
||||
self._remove_file_after_delay(file)
|
||||
return 1
|
||||
|
||||
|
||||
@ -172,19 +173,6 @@ class UnixViewer(Viewer):
|
||||
command = self.get_command_ex(file, **options)[0]
|
||||
return f"({command} {quote(file)}; rm -f {quote(file)})&"
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
fd, path = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "w") as f:
|
||||
f.write(file)
|
||||
with open(path) as f:
|
||||
command = self.get_command_ex(file, **options)[0]
|
||||
subprocess.Popen(
|
||||
["im=$(cat);" + command + " $im; rm -f $im"], shell=True, stdin=f
|
||||
)
|
||||
os.remove(path)
|
||||
return 1
|
||||
|
||||
|
||||
class XDGViewer(UnixViewer):
|
||||
"""
|
||||
@ -195,6 +183,12 @@ class XDGViewer(UnixViewer):
|
||||
command = executable = "xdg-open"
|
||||
return command, executable
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
subprocess.Popen(["xdg-open", file])
|
||||
self._remove_file_after_delay(file)
|
||||
return 1
|
||||
|
||||
|
||||
class DisplayViewer(UnixViewer):
|
||||
"""
|
||||
@ -208,6 +202,17 @@ class DisplayViewer(UnixViewer):
|
||||
command += f" -name {quote(title)}"
|
||||
return command, executable
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
args = ["display"]
|
||||
if "title" in options:
|
||||
args += ["-name", options["title"]]
|
||||
args.append(file)
|
||||
|
||||
subprocess.Popen(args)
|
||||
os.remove(file)
|
||||
return 1
|
||||
|
||||
|
||||
class GmDisplayViewer(UnixViewer):
|
||||
"""The GraphicsMagick ``gm display`` command."""
|
||||
@ -217,6 +222,12 @@ class GmDisplayViewer(UnixViewer):
|
||||
command = "gm display"
|
||||
return command, executable
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
subprocess.Popen(["gm", "display", file])
|
||||
os.remove(file)
|
||||
return 1
|
||||
|
||||
|
||||
class EogViewer(UnixViewer):
|
||||
"""The GNOME Image Viewer ``eog`` command."""
|
||||
@ -226,6 +237,12 @@ class EogViewer(UnixViewer):
|
||||
command = "eog -n"
|
||||
return command, executable
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
subprocess.Popen(["eog", "-n", file])
|
||||
os.remove(file)
|
||||
return 1
|
||||
|
||||
|
||||
class XVViewer(UnixViewer):
|
||||
"""
|
||||
@ -241,6 +258,17 @@ class XVViewer(UnixViewer):
|
||||
command += f" -name {quote(title)}"
|
||||
return command, executable
|
||||
|
||||
def show_file(self, file, **options):
|
||||
"""Display given file"""
|
||||
args = ["xv"]
|
||||
if "title" in options:
|
||||
args += ["-name", options["title"]]
|
||||
args.append(file)
|
||||
|
||||
subprocess.Popen(args)
|
||||
os.remove(file)
|
||||
return 1
|
||||
|
||||
|
||||
if sys.platform not in ("win32", "darwin"): # unixoids
|
||||
if shutil.which("xdg-open"):
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
# Master version for Pillow
|
||||
__version__ = "9.0.0"
|
||||
__version__ = "9.0.1"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user