PyPI / pillow / 13.0.0

pillow 13.0.0

Source
https://github.com/python-pillow/Pillow
Artifact scanned
pillow-13.0.0.tar.gz (sdist · 44.9 MB · SHA-256 cdcd1ba25817…)
Compared with
pillow-12.3.0.tar.gz (4 signals introduced since)
Files
581 scanned, 1147 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 61 info
Scan time
41.4 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 56 of 56 warn and critical signals; 56 demoted to info.
Disclude version
4.3.0
Scanned (UTC)
2026-10-23 01:58

Signals

Ordered by severity, then signals introduced in this release. Hidden characters are shown as ⟨U+…⟩.

  1. info warn → info by review encoding-base64 Tests/test_file_libtiff.py:1490:19 new in this release

    base64-like blob (56 bytes, compression ratio 1.20) (raw pass, confidence 0.60)

                    b"rq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna"
                    b"29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+foAAQIDBJ9Bepo="
                ),
                # 16x16 strip with duplicated
    likely benign LLM, confidence 0.95 Base64-encoded test image data in a libtiff test.
    Reasoning

    Base64 blob in a test fixture encoding a small TIFF image; a test data literal in an image library is expected.

  2. info warn → info by review identifier-confusable-collision src/libImaging/Draw.c:248:9 new in this release

    two identifiers collapse to visual skeleton `y|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        int x1,
        int y1,
        int ink,
        point_handler point,
    dismissed LLM, confidence 0.92 Drawing function parameter names are flagged by the confusable heuristic.
    Reasoning

    The flagged pair is the y1/yo style coordinate parameter names of a line/point drawing function. Standard numeric naming, not an attack.

  3. info warn → info by review identifier-confusable-collision src/libImaging/Mode.c:150:6 new in this release

    two identifiers collapse to visual skeleton `|MAG|NG_RAWMoDE_L_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        [IMAGING_RAWMODE_L_16B] = {"L;16B"},
        [IMAGING_RAWMODE_L_1] = {"L;1"},
        [IMAGING_RAWMODE_L_2] = {"L;2"},
        [IMAGING_RAWMODE_L_2I] = {"L;2I"},
    likely benign LLM, confidence 0.96 Pillow L-mode rawmode table entries flagged for identifier skeleton collision.
    Reasoning

    Ordinary Pillow L-mode rawmode table entries (L;1, L;2I, L;16B) with normal ASCII identifiers; the collision is a false positive from the 1/l/I equivalence.

  4. info warn → info by review identifier-confusable-collision src/libImaging/Mode.h:148:5 new in this release

    two identifiers collapse to visual skeleton `|MAG|NG_RAWMoDE_L_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        IMAGING_RAWMODE_LX,
        IMAGING_RAWMODE_L_1,
        IMAGING_RAWMODE_L_16,
        IMAGING_RAWMODE_L_16B,
    likely benign LLM, confidence 0.96 Pillow L-mode rawmode constant declarations in Mode.h.
    Reasoning

    Header enum entries for L-mode rawmodes; ordinary declarations flagged only due to skeleton collision.

  5. info warn → info by review payload-bytes-literal Tests/test_file_tiff_metadata.py:437:9

    concatenated bytes literal contains 48 `\xNN` escapes (95% of content) — binary payload shape (ast pass, confidence 0.85)

        data = io.BytesIO(
            b"II*\x00\x08\x00\x00\x00\x03\x00\x1a\x01\x05\x00\x00\x00\x00\x00"
            b"\x00\x00\x00\x00\x1b\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00"
            b"\x98\x82\x02\x00\x07\x00\x00\x002\x00\x00\x00\x00\x00\x00\x00a "
    likely benign LLM, confidence 0.93 Binary TIFF test fixture built from a bytes literal.
    Reasoning

    Bytes literal with \x escapes forming a TIFF header and tags is the expected shape for an image-parsing test fixture.

  6. info warn → info by review payload-bytes-literal Tests/test_pickle.py:170:9

    concatenated bytes literal contains 32 `\xNN` escapes (75% of content) — binary payload shape (ast pass, confidence 0.85)

        im = pickle.loads(
            b"\x80\x04\x95@\x00\x00\x00\x00\x00\x00\x00\x8c\x12PIL.PngImagePlugin"
            b"\x94\x8c\x0cPngImageFile\x94\x93\x94)\x81\x94]\x94(}\x94\x8c\x01L\x94K\x01"
            b"K\x01\x86\x94NC\x01\x00\x94eb."
    likely benign LLM, confidence 0.93 Pickle byte string fixture for PIL pickling tests.
    Reasoning

    Pickled PIL PngImageFile object used to test pickling round-trips; the payload is a legitimate test fixture for the library's own class.

  7. info dynamic-execution depends/install_libavif.sh:32:8

    command name is a variable (`$PKGCONFIG`) that this file sets only to plain text (ast pass, confidence 0.40)

    
        if $PKGCONFIG --exists aom; then
            LIBAVIF_CMAKE_FLAGS+=(-DAVIF_CODEC_AOM=SYSTEM)
            HAS_ENCODER=1
  8. info dynamic-execution depends/install_libavif.sh:38:8

    command name is a variable (`$PKGCONFIG`) that this file sets only to plain text (ast pass, confidence 0.40)

    
        if $PKGCONFIG --exists dav1d; then
            LIBAVIF_CMAKE_FLAGS+=(-DAVIF_CODEC_DAV1D=SYSTEM)
            HAS_DECODER=1
  9. info dynamic-execution depends/install_libavif.sh:43:8

    command name is a variable (`$PKGCONFIG`) that this file sets only to plain text (ast pass, confidence 0.40)

    
        if $PKGCONFIG --exists libgav1; then
            LIBAVIF_CMAKE_FLAGS+=(-DAVIF_CODEC_LIBGAV1=SYSTEM)
            HAS_DECODER=1
  10. info dynamic-execution depends/install_libavif.sh:48:8

    command name is a variable (`$PKGCONFIG`) that this file sets only to plain text (ast pass, confidence 0.40)

    
        if $PKGCONFIG --exists rav1e; then
            LIBAVIF_CMAKE_FLAGS+=(-DAVIF_CODEC_RAV1E=SYSTEM)
            HAS_ENCODER=1
  11. info dynamic-execution depends/install_libavif.sh:53:8

    command name is a variable (`$PKGCONFIG`) that this file sets only to plain text (ast pass, confidence 0.40)

    
        if $PKGCONFIG --exists SvtAv1Enc; then
            LIBAVIF_CMAKE_FLAGS+=(-DAVIF_CODEC_SVT=SYSTEM)
            HAS_ENCODER=1
  12. info warn → info by review dynamic-attribute setup.py:523:20

    reach-by-name through `globals` (ast pass, confidence 0.60)

            }.items():
                root = globals()[root_name]
    
                if root is None and root_name in os.environ:
    likely benign LLM, confidence 0.85 Build script resolves a module name via globals() during feature detection.
    Reasoning

    Looks up a root module by name from globals() in the build script's dependency/feature detection, reading environment variables; a plausible build-config pattern with no network or exec, though dynamic lookup merits a glance.

  13. info warn → info by review dynamic-attribute src/PIL/BmpImagePlugin.py:80:9

    reach-by-name through `vars` (ast pass, confidence 0.60)

        for k, v in COMPRESSIONS.items():
            vars()[k] = v
    
        def _bitmap(self, header: int = 0, offset: int = 0) -> None:
    likely benign LLM, confidence 0.88 Populates module globals from a static compression table.
    Reasoning

    Module-level loop populates constants into vars() to define names from a COMPRESSIONS table; a common, harmless metaprogramming idiom with static keys.

  14. info warn → info by review encoding-base64 src/PIL/ImageFont.py:1066:1

    base64-like blob (1700 bytes over 23 lines, compression ratio 0.70) (raw pass, confidence 0.60)

            Image.open(BytesIO(base64.b64decode(b"""
    iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u
    Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9
    M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g
    likely benign LLM, confidence 0.90 Base64-encoded sample PNG in ImageFont documentation.
    Reasoning

    Base64 in a docstring or doctest-like example encoding a small PNG image; no decode-and-execute behavior.

  15. info warn → info by review dynamic-execution src/PIL/ImageMath.py:297:21

    `compile` called on a non-literal expression (ast pass, confidence 0.70)

    
        compiled_code = compile(expression, "<string>", "eval")
    
        def scan(code: CodeType) -> None:
    likely benign LLM, confidence 0.85 Compiles the ImageMath expression string for evaluation.
    Reasoning

    ImageMath evaluates user-supplied arithmetic expressions by compiling them in eval mode with a restricted scanner that validates names; this is an intentional, sandboxed feature, though compile on input is a notable surface.

  16. info critical → info by review dynamic-execution src/PIL/ImageShow.py:125:9

    `os.system` called on a non-literal command — runtime-determined shell invocation (ast pass, confidence 0.85)

                raise FileNotFoundError
            os.system(self.get_command(path, **options))  # nosec
            return 1
    likely benign LLM, confidence 0.80 Launches an external image viewer via os.system.
    Reasoning

    os.system runs a viewer command built from get_command with file path; this is the expected image-viewer dispatch in PIL, marked nosec, and the command is internal, not attacker-controlled in normal use, though path injection is a real concern.

  17. info warn → info by review decoder-decompress-payload src/PIL/PdfParser.py:337:20

    file imports decoder module(s) codecs, zlib and calls `.decompress()` — embedded payload decompressed at runtime (ast pass, confidence 0.75)

            if filter == b"FlateDecode":
                dobj = zlib.decompressobj()
                plaintext = dobj.decompress(self.buf, max_length)
                if dobj.unconsumed_tail:
    likely benign LLM, confidence 0.90 Decompresses FlateDecode PDF stream content.
    Reasoning

    FlateDecode decompression of PDF stream data is the standard PDF filter implementation for parsing user-supplied PDFs; expected behavior.

  18. info warn → info by review encoding-octal src/PIL/PdfParser.py:645:37

    6 consecutive `\NNN` octal escapes (entropy 2.6 bits/byte) (raw pass, confidence 0.65)

        delimiter = rb"[][()<>{}/%]"
        delimiter_or_ws = rb"[][()<>{}/%\000\011\012\014\015\040]"
        whitespace = rb"[\000\011\012\014\015\040]"
        whitespace_or_hex = rb"[\000\011\012\014\015\0400-9a-fA-F]"
    likely benign LLM, confidence 0.92 Regex character class with PDF whitespace octal escapes.
    Reasoning

    Octal escapes in raw regex character classes for PDF whitespace and delimiters; a legitimate lexer pattern.

  19. info warn → info by review encoding-octal src/PIL/PdfParser.py:646:22

    6 consecutive `\NNN` octal escapes (entropy 2.6 bits/byte) (raw pass, confidence 0.65)

        delimiter_or_ws = rb"[][()<>{}/%\000\011\012\014\015\040]"
        whitespace = rb"[\000\011\012\014\015\040]"
        whitespace_or_hex = rb"[\000\011\012\014\015\0400-9a-fA-F]"
        whitespace_optional = whitespace + b"*"
    likely benign LLM, confidence 0.92 Regex character class with PDF whitespace octal escapes.
    Reasoning

    Same PDF whitespace regex character class; legitimate lexer code.

  20. info warn → info by review encoding-octal src/PIL/PdfParser.py:647:29

    6 consecutive `\NNN` octal escapes (entropy 2.6 bits/byte) (raw pass, confidence 0.65)

        whitespace = rb"[\000\011\012\014\015\040]"
        whitespace_or_hex = rb"[\000\011\012\014\015\0400-9a-fA-F]"
        whitespace_optional = whitespace + b"*"
        whitespace_mandatory = whitespace + b"+"
    likely benign LLM, confidence 0.92 Regex whitespace pattern using octal escapes.
    Reasoning

    Same PDF whitespace regex pattern; legitimate lexer code.

  21. info warn → info by review decoder-decompress-payload src/PIL/PngImagePlugin.py:155:12

    file imports decoder module(s) zlib and calls `.decompress()` — embedded payload decompressed at runtime (ast pass, confidence 0.75)

    def _safe_zlib_decompress(s: bytes) -> bytes:
        dobj = zlib.decompressobj()
        plaintext = dobj.decompress(s, MAX_TEXT_CHUNK)
        if dobj.unconsumed_tail:
    likely benign LLM, confidence 0.90 Size-limited zlib decompression of PNG text chunks.
    Reasoning

    Bounded zlib decompression of PNG text chunks with a max length, the standard safe handling of PNG zTXt/iTXt data.

  22. info warn → info by review payload-bytes-literal src/PIL/WalImageFile.py:86:5

    concatenated bytes literal contains 768 `\xNN` escapes (100% of content) — binary payload shape (ast pass, confidence 0.85)

        # default palette taken from piffo 0.93 by Hans Häggström
        b"\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e"
        b"\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f"
        b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c"
    likely benign LLM, confidence 0.95 Default 256-colour palette constant embedded as a bytes literal in the WAL image reader.
    Reasoning

    The bytes literal is a 768-byte default palette (grayscale-ish ramp from 0x01 upward) with a comment crediting its source. Pillow's WAL reader ships exactly this kind of constant table. Binary-shape flagging on a palette is a false positive.

  23. info warn → info by review encoding-hex src/PIL/WalImageFile.py:89:7

    16 consecutive `\xNN` escapes (entropy 3.6 bits/byte) (raw pass, confidence 0.65)

        b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c"
        b"\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b"
        b"\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10"
        b"\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07"
    likely benign LLM, confidence 0.93 Continuation of the default palette byte table.
    Reasoning

    Raw-pass hex-escape run inside the same palette constant. The entropy is low and the values form a smooth ramp, which is typical of palette data rather than an encoded payload.

  24. info warn → info by review encoding-hex src/PIL/WalImageFile.py:95:7

    5 rows of escape runs, lines 95-101 (first: 16 consecutive `\xNN` escapes (entropy 4.0 bits/byte)) (raw pass, confidence 0.65)

        b"\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d"
        b"\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31"
        b"\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28"
        b"\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07"
    likely benign LLM, confidence 0.93 Further rows of the default palette constant.
    Reasoning

    Raw-pass escape rows within the same palette table. Structure and values match colour data, and nothing here executes or decodes anything.

  25. info warn → info by review encoding-hex src/PIL/WalImageFile.py:105:7

    16 consecutive `\xNN` escapes (entropy 3.6 bits/byte) (raw pass, confidence 0.65)

        b"\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14"
        b"\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f"
        b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34"
        b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d"
    likely benign LLM, confidence 0.93 Palette byte rows in the WAL reader.
    Reasoning

    Same palette constant, continued. No decoding or execution is present, and the byte pattern is a colour table.

  26. info warn → info by review encoding-hex src/PIL/WalImageFile.py:107:7

    16 consecutive `\xNN` escapes (entropy 4.0 bits/byte) (raw pass, confidence 0.65)

        b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34"
        b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d"
        b"\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14"
        b"\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01"
    likely benign LLM, confidence 0.93 Palette byte rows in the WAL reader.
    Reasoning

    Palette data continues here. It is a static lookup table in the image reader, not a dropper blob.

  27. info warn → info by review encoding-hex src/PIL/WalImageFile.py:113:7

    8 rows of escape runs, lines 113-125 (first: 16 consecutive `\xNN` escapes (entropy 3.5 bits/byte)) (raw pass, confidence 0.65)

        b"\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01"
        b"\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27"
        b"\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c"
        b"\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a"
    likely benign LLM, confidence 0.93 Palette byte rows in the WAL reader.
    Reasoning

    Raw hex-escape rows form a monotone colour ramp typical of a palette. No execution or decoding follows the literal.

  28. info warn → info by review encoding-hex src/PIL/WalImageFile.py:132:7

    16 consecutive `\xNN` escapes (entropy 3.9 bits/byte) (raw pass, confidence 0.65)

        b"\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10"
        b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b"
        b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20"
    )
    likely benign LLM, confidence 0.93 Closing rows of the default palette constant.
    Reasoning

    Final rows of the palette literal, closing the parenthesised constant. Nothing about it indicates a payload.

  29. info warn → info by review encoding-hex src/PIL/WalImageFile.py:133:7

    16 consecutive `\xNN` escapes (entropy 3.9 bits/byte) (raw pass, confidence 0.65)

        b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b"
        b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20"
    )
    likely benign LLM, confidence 0.93 Tail of the default palette constant.
    Reasoning

    Last rows of the same palette constant. It is static colour data.

  30. info critical → info by review dynamic-import src/PIL/features.py:42:9

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

        try:
            __import__(module)
            return True
        except ModuleNotFoundError:
    likely benign LLM, confidence 0.88 Optional-module availability probe using __import__ with a variable name.
    Reasoning

    In Pillow's features.check_module, __import__ is called on a module name to test whether an optional dependency is installed. The name comes from a fixed set of internal module names, not untrusted input, and the ModuleNotFoundError handler confirms the intended use.

  31. info critical → info by review dynamic-import src/PIL/features.py:63:20

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

    
        return getattr(__import__(module, fromlist=[ver]), ver)
    likely benign LLM, confidence 0.88 Dynamic import to read an optional library's version attribute.
    Reasoning

    Version lookup imports a known optional dependency by name and reads its version attribute. This is the standard way Pillow reports library versions and does not take attacker-controlled input.

  32. info critical → info by review dynamic-import src/PIL/features.py:153:27

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

        try:
            imported_module = __import__(module, fromlist=["PIL"])
            return getattr(imported_module, flag)
        except ModuleNotFoundError:
    likely benign LLM, confidence 0.88 Feature-flag lookup via dynamic import of an internal module.
    Reasoning

    Feature check imports a Pillow-internal module named by the caller-side table and reads a flag attribute. Dynamic import of a fixed internal name is a normal feature-detection pattern.

  33. info critical → info by review dynamic-import src/PIL/features.py:176:20

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

    
        return getattr(__import__(module, fromlist=[ver]), ver)
    likely benign LLM, confidence 0.88 Dynamic import for dependency version reporting.
    Reasoning

    Same version-lookup helper as line 63, importing a known dependency and reading its version. Legitimate and not reachable from untrusted data.

  34. info warn → info by review dynamic-attribute src/Tk/tkImaging.c:387:12

    `dlsym` called with a non-literal symbol name (ast pass, confidence 0.75)

        dlerror();
        func = dlsym(lib_handle, func_name);
        if (func == NULL) {
            const char *error = dlerror();
    likely benign LLM, confidence 0.85 Runtime resolution of Tk shared-library function pointers via dlsym.
    Reasoning

    Pillow's Tk imaging module resolves Tk library functions at runtime with dlsym on Unix so it does not link against libtk directly. The symbol names are fixed internal constants, and the NULL check and dlerror handling are standard for this pattern.

  35. info warn → info by review dynamic-import src/Tk/tkImaging.c:436:20

    `dlopen` called with a non-literal path (ast pass, confidence 0.75)

        /* Try loading from the main program namespace first */
        main_program = dlopen(NULL, RTLD_LAZY);
        if (_func_loader(main_program) == 0) {
            dlclose(main_program);
    likely benign LLM, confidence 0.85 dlopen(NULL) probes the main program's namespace for the Tk/Tkinter loader function.
    Reasoning

    dlopen(NULL) is the standard way to look up symbols in the main program, a common pattern in Python C extensions. The snippet shows no attacker-controlled path or follow-on payload, and the file is Pillow's Tk integration code.

  36. info warn → info by review dynamic-import src/Tk/tkImaging.c:462:19

    `dlopen` called with a non-literal path (ast pass, confidence 0.75)

        }
        tkinter_lib = dlopen(tkinter_libname, RTLD_LAZY);
        if (tkinter_lib == NULL) {
            PyErr_SetString(PyExc_RuntimeError, "Cannot dlopen tkinter module file");
    likely benign LLM, confidence 0.85 dlopen loads the tkinter extension module file, using a path resolved at runtime.
    Reasoning

    The path comes from a variable naming the tkinter shared library, which Pillow locates at runtime to bind to Tk. The non-literal path is expected for this kind of dynamic loader, and the snippet shows only error handling.

  37. info warn → info by review identifier-confusable-collision src/libImaging/Access.c:101:6

    two identifiers collapse to visual skeleton `|MAG|NG_MoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

    static struct ImagingAccessInstance accessors[] = {
        {IMAGING_MODE_1, get_pixel_8, put_pixel_8},
        {IMAGING_MODE_L, get_pixel_8, put_pixel_8},
        {IMAGING_MODE_LA, get_pixel_32_2bands, put_pixel_32},
    dismissed LLM, confidence 0.92 Mode constant names in an accessor table trip the confusable-identifier heuristic.
    Reasoning

    The collision is between identifiers like IMAGING_MODE_1 and IMAGING_MODE_L, which differ only by digit/letter confusables in an all-caps mode-name set. That is a naming artifact of the token pass, not obfuscation.

  38. info warn → info by review identifier-confusable-collision src/libImaging/Convert.c:1068:17

    two identifiers collapse to visual skeleton `|MAG|NG_MoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

    
        if (mode == IMAGING_MODE_1) {
            convert = alpha ? pa2bit : p2bit;
        } else if (mode == IMAGING_MODE_L) {
    dismissed LLM, confidence 0.92 Image mode constants in a conversion dispatch are flagged by the confusable heuristic.
    Reasoning

    The flagged identifiers are ordinary IMAGING_MODE_* constants compared in a mode dispatch, which the confusable heuristic falsely matches. No hidden or homoglyph-based code is shown.

  39. info warn → info by review identifier-confusable-collision src/libImaging/Draw.c:82:9

    two identifiers collapse to visual skeleton `xo` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        int d;
        int x0, y0;
        int xmin, ymin, xmax, ymax;
        float dx;
    dismissed LLM, confidence 0.92 Short coordinate variable names collide visually under the confusable heuristic.
    Reasoning

    The skeleton collision is between ordinary short coordinate names xo and yo (x0/xO-style pairs) in standard line-drawing variables. This is a naming false positive.

  40. info warn → info by review identifier-confusable-collision src/libImaging/Draw.c:82:13

    two identifiers collapse to visual skeleton `yo` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        int d;
        int x0, y0;
        int xmin, ymin, xmax, ymax;
        float dx;
    dismissed LLM, confidence 0.92 Coordinate variable names in a declaration are flagged by the confusable heuristic.
    Reasoning

    Same as the other finding in this declaration: the flagged pair is ordinary coordinate variable names (y0/y1/ymin style) within a standard drawing routine.

  41. info warn → info by review identifier-confusable-collision src/libImaging/Draw.c:127:40

    two identifiers collapse to visual skeleton `x|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

    static inline void
    hline8(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
        if (y0 >= 0 && y0 < im->ysize) {
            if (x0 < 0) {
    dismissed LLM, confidence 0.92 Parameter names in a horizontal-line drawing helper trip the confusable heuristic.
    Reasoning

    The flagged identifiers are ordinary hline8 parameters and locals (x0, x1 and related names). The visual collision is a benign artifact of digit and letter shapes.

  42. info warn → info by review identifier-confusable-collision src/libImaging/Draw.c:937:20

    two identifiers collapse to visual skeleton `c|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        int32_t py, pl, pr;
        int32_t cy[4], cl[4], cr[4];
        int8_t bufcnt;
        int8_t finished;
    dismissed LLM, confidence 0.92 Scanline buffer variable names are flagged by the confusable heuristic.
    Reasoning

    The flagged identifiers are ordinary array and buffer names (cy, cl, cr with bufcnt/finished) in a polygon/scanline routine. The collision is a naming false positive.

  43. info warn → info by review identifier-confusable-collision src/libImaging/Draw.c:1202:25

    two identifiers collapse to visual skeleton `a|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

    void
    normalize_angles(float *al, float *ar) {
        if (*ar - *al >= 360) {
            *al = 0;
    dismissed LLM, confidence 0.92 Angle parameter names in normalize_angles are flagged by the confusable heuristic.
    Reasoning

    The flagged names are the al/ar angle parameters of normalize_angles, a plain geometric helper. The collision is a visual artifact of short identifiers.

  44. info warn → info by review identifier-confusable-collision src/libImaging/Fill.c:87:17

    two identifiers collapse to visual skeleton `|MAG|NG_MoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

    
        if (mode != IMAGING_MODE_1 && mode != IMAGING_MODE_F && mode != IMAGING_MODE_I &&
            mode != IMAGING_MODE_L && mode != IMAGING_MODE_P) {
            return (Imaging)ImagingError_ModeError();
    dismissed LLM, confidence 0.92 Mode constants in a validity check are flagged by the confusable heuristic.
    Reasoning

    The flagged identifiers are the IMAGING_MODE_* constants used in a mode validity check, a standard pattern. The skeleton collision is a benign naming artifact.

  45. info warn → info by review identifier-confusable-collision src/libImaging/Geometry.c:800:9

    two identifiers collapse to visual skeleton `xo` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        Imaging imIn,
        int x0,
        int y0,
        int x1,
    dismissed LLM, confidence 0.92 Coordinate parameters in a geometry function are flagged by the confusable heuristic.
    Reasoning

    The flagged names are the x0/x1 coordinate parameters of a geometry transform function. This is ordinary numeric naming with no hidden or homoglyph content.

  46. info warn → info by review identifier-confusable-collision src/libImaging/Geometry.c:801:9

    two identifiers collapse to visual skeleton `yo` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        int x0,
        int y0,
        int x1,
        int y1,
    dismissed LLM, confidence 0.92 Coordinate parameters in a geometry function are flagged by the confusable heuristic.
    Reasoning

    The flagged names are the y0/y1 coordinate parameters that accompany the x-coordinates in the same geometry function. Standard naming, not obfuscation.

  47. info warn → info by review macro-keyword-override src/libImaging/ImPlatform.h:52:1

    `#define FLOAT32 float` aliases a C reserved keyword behind a non-keyword macro name (ast pass, confidence 0.85)

    #define FLOAT16 UINT16
    #define FLOAT32 float
    #define FLOAT64 double
    likely benign LLM, confidence 0.90 A platform header defines FLOAT32 as an alias for float.
    Reasoning

    Mapping FLOAT32 to float is a standard portable-typedef macro in Pillow's platform header. It is not a dangerous keyword override, and the header is the usual place for such definitions.

  48. info warn → info by review macro-keyword-override src/libImaging/ImPlatform.h:53:1

    `#define FLOAT64 double` aliases a C reserved keyword behind a non-keyword macro name (ast pass, confidence 0.85)

    #define FLOAT32 float
    #define FLOAT64 double
    
    #ifdef _MSC_VER
    likely benign LLM, confidence 0.90 A platform header defines FLOAT64 as an alias for double.
    Reasoning

    Mapping FLOAT64 to double is the same portable type-alias macro as FLOAT32 in the platform header. It is ordinary configuration with no malicious effect.

  49. info warn → info by review identifier-confusable-collision src/libImaging/Mode.c:7:6

    two identifiers collapse to visual skeleton `|MAG|NG_MoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

    
        [IMAGING_MODE_1] = {"1"},         [IMAGING_MODE_CMYK] = {"CMYK"},
        [IMAGING_MODE_F] = {"F"},         [IMAGING_MODE_HSV] = {"HSV"},
        [IMAGING_MODE_I] = {"I"},         [IMAGING_MODE_L] = {"L"},
    likely benign LLM, confidence 0.97 Pillow image mode enum constants in Mode.c whose names collide under confusable-skeleton normalization.
    Reasoning

    Pillow's legitimate IMAGING_MODE_* enum names are visually confusable only because they contain 'I' and 'l' letters in identifiers like IMAGING_MODE_I and IMAGING_MODE_L; this is a naming artifact of the token skeleton normalization, not an attack. Plain ASCII, normal C enum definitions.

  50. info warn → info by review identifier-confusable-collision src/libImaging/Mode.c:44:6

    two identifiers collapse to visual skeleton `|MAG|NG_RAWMoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

    
        [IMAGING_RAWMODE_1] = {"1"},
        [IMAGING_RAWMODE_CMYK] = {"CMYK"},
        [IMAGING_RAWMODE_F] = {"F"},
    likely benign LLM, confidence 0.97 Pillow raw mode table entries whose identifiers collide under the skeleton check.
    Reasoning

    RAWMODE enum entries with ordinary ASCII names like IMAGING_RAWMODE_1 and IMAGING_RAWMODE_I; the collision comes from the normalization of I/1/l characters, not from hidden code.

  51. info warn → info by review identifier-confusable-collision src/libImaging/Mode.c:70:6

    two identifiers collapse to visual skeleton `|MAG|NG_RAWMoDE_|_8` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

    
        [IMAGING_RAWMODE_1_8] = {"1;8"},
        [IMAGING_RAWMODE_1_I] = {"1;I"},
        [IMAGING_RAWMODE_1_IR] = {"1;IR"},
    likely benign LLM, confidence 0.96 Pillow rawmode initializers for 1-bit variants colliding under skeleton normalization.
    Reasoning

    Same pattern: standard Pillow rawmode identifiers such as IMAGING_RAWMODE_1_I and _1_8 in a plain string-mapping table.

  52. info warn → info by review identifier-confusable-collision src/libImaging/Mode.h:7:5

    two identifiers collapse to visual skeleton `|MAG|NG_MoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

    
        IMAGING_MODE_1,
        IMAGING_MODE_CMYK,
        IMAGING_MODE_F,
    likely benign LLM, confidence 0.96 Pillow image mode enum declarations in Mode.h.
    Reasoning

    Header enum declaring IMAGING_MODE_1, IMAGING_MODE_CMYK etc. Standard Pillow declarations; the skeleton collision is spurious.

  53. info warn → info by review identifier-confusable-collision src/libImaging/Mode.h:43:5

    two identifiers collapse to visual skeleton `|MAG|NG_RAWMoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        // Non-rawmode aliases.
        IMAGING_RAWMODE_1,
        IMAGING_RAWMODE_CMYK,
        IMAGING_RAWMODE_F,
    likely benign LLM, confidence 0.96 Pillow rawmode alias enum declarations in Mode.h.
    Reasoning

    Header enum of rawmode aliases with ordinary names; collision is a normalization false positive.

  54. info warn → info by review identifier-confusable-collision src/libImaging/Mode.h:67:5

    two identifiers collapse to visual skeleton `|MAG|NG_RAWMoDE_|_8` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        // Rawmodes
        IMAGING_RAWMODE_1_8,
        IMAGING_RAWMODE_1_I,
        IMAGING_RAWMODE_1_IR,
    likely benign LLM, confidence 0.96 Pillow rawmode constant declarations in Mode.h.
    Reasoning

    Header enum of rawmode constants like IMAGING_RAWMODE_1_8 and _1_IR; ordinary declarations with no hidden content.

  55. info warn → info by review identifier-confusable-collision src/libImaging/Pack.c:542:6

    two identifiers collapse to visual skeleton `|MAG|NG_MoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        /* bilevel */
        {IMAGING_MODE_1, IMAGING_RAWMODE_1, 1, pack1},
        {IMAGING_MODE_1, IMAGING_RAWMODE_1_I, 1, pack1I},
        {IMAGING_MODE_1, IMAGING_RAWMODE_1_R, 1, pack1R},
    likely benign LLM, confidence 0.96 Pillow pack dispatch table rows referencing IMAGING_MODE_1 and rawmode constants.
    Reasoning

    Pack table rows mapping mode and rawmode constants to packer functions; standard Pillow code with normal identifiers.

  56. info warn → info by review identifier-confusable-collision src/libImaging/Pack.c:542:22

    two identifiers collapse to visual skeleton `|MAG|NG_RAWMoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        /* bilevel */
        {IMAGING_MODE_1, IMAGING_RAWMODE_1, 1, pack1},
        {IMAGING_MODE_1, IMAGING_RAWMODE_1_I, 1, pack1I},
        {IMAGING_MODE_1, IMAGING_RAWMODE_1_R, 1, pack1R},
    likely benign LLM, confidence 0.96 Pillow pack dispatch table entries referencing rawmode constants.
    Reasoning

    Same pack dispatch table; the flagged rawmode identifiers are normal enum references.

  57. info warn → info by review identifier-confusable-collision src/libImaging/Storage.c:65:17

    two identifiers collapse to visual skeleton `|MAG|NG_MoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

    
        if (mode == IMAGING_MODE_1) {
            /* 1-bit images */
            im->bands = im->pixelsize = 1;
    likely benign LLM, confidence 0.96 Storage initialization branch for 1-bit image mode.
    Reasoning

    Simple comparison of mode against IMAGING_MODE_1 to set bands and pixelsize for 1-bit images; ordinary logic.

  58. info warn → info by review identifier-confusable-collision src/libImaging/Unpack.c:249:1

    two identifiers collapse to visual skeleton `unpack|8` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

    static void
    unpack18(UINT8 *out, const UINT8 *in, int pixels) {
        /* Unpack a '|b1' image, which is a numpy boolean.
           1 == true, 0==false, in bytes */
    likely benign LLM, confidence 0.90 Pillow unpacker function unpack18 for numpy boolean images.
    Reasoning

    Function unpack18 is a 1-bit numpy boolean unpacker; the name and comment mention '|b1' which is a numpy dtype string. The collision is between unpack18 and a similar name under normalization, a benign naming artifact in a documented function.

  59. info warn → info by review identifier-confusable-collision src/libImaging/Unpack.c:1562:6

    two identifiers collapse to visual skeleton `|MAG|NG_MoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        /* bilevel */
        {IMAGING_MODE_1, IMAGING_RAWMODE_1, 1, unpack1},
        {IMAGING_MODE_1, IMAGING_RAWMODE_1_I, 1, unpack1I},
        {IMAGING_MODE_1, IMAGING_RAWMODE_1_R, 1, unpack1R},
    likely benign LLM, confidence 0.96 Pillow unpack dispatch table rows for bilevel modes.
    Reasoning

    Unpack dispatch table rows with normal Pillow mode and rawmode constants and unpacker function names.

  60. info warn → info by review identifier-confusable-collision src/libImaging/Unpack.c:1562:22

    two identifiers collapse to visual skeleton `|MAG|NG_RAWMoDE_|` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        /* bilevel */
        {IMAGING_MODE_1, IMAGING_RAWMODE_1, 1, unpack1},
        {IMAGING_MODE_1, IMAGING_RAWMODE_1_I, 1, unpack1I},
        {IMAGING_MODE_1, IMAGING_RAWMODE_1_R, 1, unpack1R},
    likely benign LLM, confidence 0.96 Pillow unpack dispatch table entries referencing rawmode constants.
    Reasoning

    Same unpack dispatch table; rawmode identifiers are ordinary enum references.

  61. info warn → info by review identifier-confusable-collision src/libImaging/Unpack.c:1566:22

    two identifiers collapse to visual skeleton `|MAG|NG_RAWMoDE_|_8` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

        {IMAGING_MODE_1, IMAGING_RAWMODE_1_IR, 1, unpack1IR},
        {IMAGING_MODE_1, IMAGING_RAWMODE_1_8, 8, unpack18},
        {IMAGING_MODE_1, IMAGING_RAWMODE_L, 8, unpack18},
    likely benign LLM, confidence 0.75 A lookup table in Pillow's Unpack.c maps image modes to raw-mode unpacker functions, and two of its constant names look alike under 0/O and 1/l/I folding.
    Reasoning

    The snippet is a plain table of Pillow raw-mode unpacker entries (IMAGING_MODE_1 paired with IMAGING_RAWMODE_* constants and unpack functions). The collision is between identifiers that differ only by a visually ambiguous character, such as 1 versus I or l, which is a common naming artifact in C enums and not a sign of a deliberate homoglyph swap. Nothing in the snippet executes code or hides behaviour, and the file is a well-known image library. The residual doubt is that the snippet doesn't show the declarations of the two colliding identifiers, so a real homoglyph shadowing can't be ruled out from this excerpt alone.

Reproduce This Scan

Run disclude locally to verify. These commands scan the published file; the "new in this release" marks come from comparing it with the previous release's file, which they don't repeat. Add --format sarif for SARIF output. Add --llm (with an API key in the environment) to include the LLM review.

cargo install disclude --version 4.3.0
curl -fsSL -o pillow-13.0.0.tar.gz https://files.pythonhosted.org/packages/ce/a4/9cc30690a394a8305e8be4c9036aa16d0dfddadd09079860b582f562496d/pillow-13.0.0.tar.gz
printf '%s  %s\n' cdcd1ba258178f7356d74170c9a81ba4eab6a5d55748bc402a5a2a6fe6be3510 pillow-13.0.0.tar.gz | shasum -a 256 -c -
mkdir pkg && tar -xzf pillow-13.0.0.tar.gz -C pkg --strip-components 1
disclude scan --untrusted --severity info pkg