Merge 2e3b0d483a into 877527cefc
This commit is contained in:
commit
594fd616db
@ -128,6 +128,15 @@ class TestImage:
|
||||
assert im.mode == "RGB"
|
||||
assert im.size == (128, 128)
|
||||
|
||||
@pytest.mark.parametrize("formats", (("!PNG",), ("PNG", "!PNG"), ("JPEG", "!PNG")))
|
||||
def test_open_formats_exclude(self, formats: tuple[str]) -> None:
|
||||
with Image.open("Tests/images/hopper.jpg", formats=formats):
|
||||
pass
|
||||
|
||||
with pytest.raises(UnidentifiedImageError):
|
||||
with Image.open("Tests/images/hopper.png", formats=formats):
|
||||
pass
|
||||
|
||||
def test_open_verbose_failure(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(Image, "WARN_POSSIBLE_FORMATS", True)
|
||||
|
||||
|
||||
@ -232,8 +232,8 @@ The following mitigations are listed in priority order.
|
||||
advisories <https://github.com/python-pillow/Pillow/security/advisories>`_.
|
||||
5. **Enforce** ``MAX_IMAGE_PIXELS`` — never set it to ``None``; treat
|
||||
``Image.DecompressionBombWarning`` as an error.
|
||||
6. **Allowlist image formats** — restrict accepted formats when opening
|
||||
images, for example with ``Image.open(..., formats=...)``, and isolate
|
||||
6. **Restrict image formats** — restrict formats when opening images, for
|
||||
example with ``Image.open(..., formats=...)``, and isolate
|
||||
installs/environments if you need to minimise supported formats.
|
||||
7. **Strip metadata on output** — never pass through EXIF/XMP/ICC from user
|
||||
uploads to publicly served images.
|
||||
|
||||
@ -3595,11 +3595,17 @@ def open(
|
||||
and be opened in binary mode. The file object will also seek to zero
|
||||
before reading.
|
||||
:param mode: The mode. If given, this argument must be "r".
|
||||
:param formats: A list or tuple of formats to attempt to load the file in.
|
||||
This can be used to restrict the set of formats checked.
|
||||
Pass ``None`` to try all supported formats. You can print the set of
|
||||
available formats by running ``python3 -m PIL`` or using
|
||||
the :py:func:`PIL.features.pilinfo` function.
|
||||
:param formats: A list or tuple of formats to attempt to load the file in, for
|
||||
example, ``("JPEG", "GIF")``. This can be used to restrict the set of formats
|
||||
checked.
|
||||
|
||||
To exclude a format, start the format with "!", for example,
|
||||
``("!EPS", "!PSD")``.
|
||||
|
||||
Pass ``None`` to try all supported formats.
|
||||
|
||||
You can print the set of available formats by running ``python3 -m PIL`` or
|
||||
using the :py:func:`PIL.features.pilinfo` function.
|
||||
:returns: An :py:class:`~PIL.Image.Image` object.
|
||||
:exception FileNotFoundError: If the file cannot be found.
|
||||
:exception PIL.UnidentifiedImageError: If the image cannot be opened and
|
||||
@ -3619,11 +3625,20 @@ def open(
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
if formats is None:
|
||||
formats = ID
|
||||
elif not isinstance(formats, (list, tuple)):
|
||||
msg = "formats must be a list or tuple" # type: ignore[unreachable]
|
||||
raise TypeError(msg)
|
||||
if formats is not None:
|
||||
if not isinstance(formats, (list, tuple)):
|
||||
msg = "formats must be a list or tuple" # type: ignore[unreachable]
|
||||
raise TypeError(msg)
|
||||
|
||||
allowed = set()
|
||||
excluded = set()
|
||||
for f in formats:
|
||||
f = f.upper()
|
||||
if f.startswith("!"):
|
||||
excluded.add(f[1:])
|
||||
else:
|
||||
allowed.add(f)
|
||||
allowed -= excluded
|
||||
|
||||
exclusive_fp = False
|
||||
filename: str | bytes = ""
|
||||
@ -3654,12 +3669,15 @@ def open(
|
||||
fp: IO[bytes],
|
||||
filename: str | bytes,
|
||||
prefix: bytes,
|
||||
formats: list[str] | tuple[str, ...],
|
||||
check_formats: list[str],
|
||||
) -> ImageFile.ImageFile | None:
|
||||
for i in formats:
|
||||
i = i.upper()
|
||||
if i not in OPEN:
|
||||
init()
|
||||
if formats is not None:
|
||||
if allowed:
|
||||
check_formats = [f for f in check_formats if f in allowed]
|
||||
else:
|
||||
check_formats = [f for f in check_formats if f not in excluded]
|
||||
|
||||
for i in check_formats:
|
||||
try:
|
||||
factory, accept = OPEN[i]
|
||||
result = not accept or accept(prefix)
|
||||
@ -3670,7 +3688,12 @@ def open(
|
||||
im = factory(fp, filename)
|
||||
_decompression_bomb_check(im.size)
|
||||
return im
|
||||
except (SyntaxError, IndexError, TypeError, struct.error) as e:
|
||||
except ( # noqa: PERF203
|
||||
SyntaxError,
|
||||
IndexError,
|
||||
TypeError,
|
||||
struct.error,
|
||||
) as e:
|
||||
if WARN_POSSIBLE_FORMATS:
|
||||
warning_messages.append(i + " opening failed. " + str(e))
|
||||
except BaseException:
|
||||
@ -3679,21 +3702,13 @@ def open(
|
||||
raise
|
||||
return None
|
||||
|
||||
im = _open_core(fp, filename, prefix, formats)
|
||||
|
||||
if im is None and formats is ID:
|
||||
if not (im := _open_core(fp, filename, prefix, ID)):
|
||||
# Try preinit (few common plugins) then init (all plugins)
|
||||
for loader in (preinit, init):
|
||||
checked_formats = ID.copy()
|
||||
loader()
|
||||
if formats != checked_formats:
|
||||
im = _open_core(
|
||||
fp,
|
||||
filename,
|
||||
prefix,
|
||||
tuple(f for f in formats if f not in checked_formats),
|
||||
)
|
||||
if im is not None:
|
||||
if check_formats := [f for f in ID if f not in checked_formats]:
|
||||
if im := _open_core(fp, filename, prefix, check_formats):
|
||||
break
|
||||
|
||||
if im:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user