Merge pull request #8178 from radarhere/imageshow

Raise FileNotFoundError if show_file() path does not exist
This commit is contained in:
Andrew Murray 2024-06-29 06:17:44 +10:00 committed by GitHub
commit 8daf550b7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 46 additions and 26 deletions

View File

@ -1,5 +1,6 @@
from __future__ import annotations
import os
from typing import Any
import pytest
@ -65,6 +66,27 @@ def test_show_without_viewers() -> None:
ImageShow._viewers = viewers
@pytest.mark.parametrize(
"viewer",
(
ImageShow.Viewer(),
ImageShow.WindowsViewer(),
ImageShow.MacViewer(),
ImageShow.XDGViewer(),
ImageShow.DisplayViewer(),
ImageShow.GmDisplayViewer(),
ImageShow.EogViewer(),
ImageShow.XVViewer(),
ImageShow.IPythonViewer(),
),
)
def test_show_file(viewer: ImageShow.Viewer) -> None:
assert not os.path.exists("missing.png")
with pytest.raises(FileNotFoundError):
viewer.show_file("missing.png")
def test_viewer() -> None:
viewer = ImageShow.Viewer()

View File

@ -4,21 +4,16 @@
Security
========
TODO
^^^^
ImageShow.WindowsViewer.show_file
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TODO
If an attacker has control over the ``path`` passed to
``ImageShow.WindowsViewer.show_file()``, they may be able to
execute arbitrary shell commands.
:cve:`YYYY-XXXXX`: TODO
^^^^^^^^^^^^^^^^^^^^^^^
TODO
Backwards Incompatible Changes
==============================
TODO
^^^^
To prevent this, a :py:exc:`FileNotFoundError` will be raised if the ``path``
does not exist as a file. To provide a consistent experience, the error has
been added to all :py:class:`~PIL.ImageShow` viewers.
Deprecations
============
@ -46,14 +41,6 @@ ImageDraw.getdraw hints parameter
The ``hints`` parameter in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecated.
API Changes
===========
TODO
^^^^
TODO
API Additions
=============
@ -64,11 +51,6 @@ Added :py:meth:`~PIL.ImageDraw.ImageDraw.circle`. It provides the same functiona
:py:meth:`~PIL.ImageDraw.ImageDraw.ellipse`, but instead of taking a bounding box, it
takes a center point and radius.
TODO
^^^^
TODO
Other Changes
=============

View File

@ -118,6 +118,8 @@ class Viewer:
"""
Display given file.
"""
if not os.path.exists(path):
raise FileNotFoundError
os.system(self.get_command(path, **options)) # nosec
return 1
@ -142,6 +144,8 @@ class WindowsViewer(Viewer):
"""
Display given file.
"""
if not os.path.exists(path):
raise FileNotFoundError
subprocess.Popen(
self.get_command(path, **options),
shell=True,
@ -171,6 +175,8 @@ class MacViewer(Viewer):
"""
Display given file.
"""
if not os.path.exists(path):
raise FileNotFoundError
subprocess.call(["open", "-a", "Preview.app", path])
executable = sys.executable or shutil.which("python3")
if executable:
@ -215,6 +221,8 @@ class XDGViewer(UnixViewer):
"""
Display given file.
"""
if not os.path.exists(path):
raise FileNotFoundError
subprocess.Popen(["xdg-open", path])
return 1
@ -237,6 +245,8 @@ class DisplayViewer(UnixViewer):
"""
Display given file.
"""
if not os.path.exists(path):
raise FileNotFoundError
args = ["display"]
title = options.get("title")
if title:
@ -259,6 +269,8 @@ class GmDisplayViewer(UnixViewer):
"""
Display given file.
"""
if not os.path.exists(path):
raise FileNotFoundError
subprocess.Popen(["gm", "display", path])
return 1
@ -275,6 +287,8 @@ class EogViewer(UnixViewer):
"""
Display given file.
"""
if not os.path.exists(path):
raise FileNotFoundError
subprocess.Popen(["eog", "-n", path])
return 1
@ -299,6 +313,8 @@ class XVViewer(UnixViewer):
"""
Display given file.
"""
if not os.path.exists(path):
raise FileNotFoundError
args = ["xv"]
title = options.get("title")
if title: