PyPI / pillow / 12.3.0

pillow 12.3.0

Source
https://github.com/python-pillow/Pillow
Tag scanned
12.3.0
Compared with
12.2.0 (1 signals introduced since)
Files
598 scanned, 1152 skipped (binary, oversized, or unrecognised)
Signals
1 critical · 1 warn · 59 info
Scan time
36.7 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 56 of 56 warn and critical signals; 54 demoted to info.
Disclude version
4.0.0
Scanned (UTC)
2026-10-14 18:18

Signals

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

  1. critical dynamic-execution src/PIL/ImageShow.py:120: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
    suspicious LLM, confidence 0.60 Image viewer plugin launches an external viewer via os.system with a constructed command.
    Reasoning

    os.system runs a command built by a viewer plugin's get_command from a file path. This is a shell invocation with a runtime-determined command, but it is the standard image-viewer launch path in Pillow, and the file path is caller-supplied. The critical severity is over-stated for legitimate code, though command injection via file names is a real concern.

  2. warn 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.60 ImageMath compiles user-supplied expression strings for evaluation as a documented feature.
    Reasoning

    compile is called on the expression argument to evaluate image math expressions, which is a documented feature. The expression is a user-supplied math string, not a remote payload, but it is still dynamic code execution in a library, so it warrants a note.

  3. info warn → info by review decoder-decompress-payload src/PIL/PdfParser.py:331:20 new in this release

    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.70 PDF parser decompresses FlateDecode streams as part of normal PDF parsing.
    Reasoning

    zlib decompressobj on FlateDecode streams is the standard PDF filter implementation for parsing PDF content, not a hidden payload dropper.

  4. info warn → info by review dynamic-execution Tests/oss-fuzz/test_fuzzers.py:27:5

    `subprocess.check_output` called with `shell=True` and a literal command (ast pass, confidence 0.70)

        "path",
        subprocess.check_output("find Tests/images -type f", shell=True).split(b"\n"),
    )
    def test_fuzz_images(path: str) -> None:
    likely benign LLM, confidence 0.85 Test helper runs a literal find command via shell to list fuzz corpus files.
    Reasoning

    Test harness running a fixed find command with shell=True to enumerate test image files; the command is a literal with no external input, typical of oss-fuzz test setup.

  5. info warn → info by review dynamic-execution Tests/oss-fuzz/test_fuzzers.py:54:13

    `subprocess.check_output` called with `shell=True` and a literal command (ast pass, confidence 0.70)

    @pytest.mark.parametrize(
        "path", subprocess.check_output("find Tests/fonts -type f", shell=True).split(b"\n")
    )
    def test_fuzz_fonts(path: str) -> None:
    likely benign LLM, confidence 0.85 Test parametrization runs a literal find command via shell to enumerate font files.
    Reasoning

    Same pattern as the sibling finding: a literal find command in a test parametrization over font test files, no untrusted input.

  6. info warn → info by review payload-bytes-literal Tests/test_file_tiff_metadata.py:450: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.90 Test fixture embedding a small binary TIFF file as a bytes literal.
    Reasoning

    A bytes literal of a TIFF file header and structure is used as in-memory test input to BytesIO; escape density is natural for binary fixtures.

  7. info warn → info by review payload-bytes-literal Tests/test_pickle.py:167: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.90 Test fixture containing a serialized PIL PngImageFile pickle as a bytes literal.
    Reasoning

    A pickle byte stream for a PIL PNG image is used as a regression test input for pickle.loads; this is test data for a known image-pickling format.

  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. info warn → info by review dynamic-attribute setup.py:520: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.70 Build script looks up library root objects by name through globals().
    Reasoning

    Reads a root object from globals() by a name drawn from a fixed dict of library roots during build-time dependency detection. Dynamic but benign in a setup script; the name source is internal, not attacker-controlled.

  14. info warn → info by review dynamic-attribute src/PIL/BmpImagePlugin.py:75: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.75 Module-level loop assigns compression constants into vars() from a static dict.
    Reasoning

    Populates module-level names from the COMPRESSIONS dict at import time, a common Python idiom for generating constants. The keys are fixed in-source.

  15. info warn → info by review encoding-base64 src/PIL/ImageFont.py:1061: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.80 Base64-encoded PNG image embedded in source, likely in a docstring or example.
    Reasoning

    A base64 PNG blob in a docstring or doctest-like example of an embedded image; the raw pass has a high false-positive rate for base64 image data in documentation.

  16. info warn → info by review encoding-octal src/PIL/PdfParser.py:635: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.85 Regex character class using octal escapes for PDF delimiter and whitespace bytes.
    Reasoning

    Octal escapes in a regex character class define PDF whitespace and delimiter sets; this is the expected way to write those control bytes in a raw regex.

  17. info warn → info by review encoding-octal src/PIL/PdfParser.py:636: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.85 Regex patterns using octal escapes for PDF whitespace and delimiter bytes.
    Reasoning

    Same as the sibling finding: octal escapes for PDF whitespace bytes in regex patterns, a legitimate parser idiom.

  18. info warn → info by review encoding-octal src/PIL/PdfParser.py:637: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.95 A bytes regex character class listing PDF whitespace control characters via octal escapes.
    Reasoning

    Octal escapes inside a regex character class for PDF whitespace bytes (NUL, tab, LF, FF, CR, space) are a standard, readable way to express those control bytes in a bytes regex. Nothing is decoded or executed.

  19. info warn → info by review decoder-decompress-payload src/PIL/PngImagePlugin.py:146: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.93 Size-limited zlib decompression of PNG text chunk data.
    Reasoning

    This is Pillow's bounded zlib decompression for PNG text chunks (iTXt/zTXt), capped by MAX_TEXT_CHUNK to prevent decompression bombs. It decompresses the image file's own chunk data, which is the expected format behavior, not a hidden embedded payload.

  20. info warn → info by review payload-bytes-literal src/PIL/WalImageFile.py:81: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 A default RGB palette table embedded as a bytes constant in the WAL image loader.
    Reasoning

    This is a default 256-colour palette (768 bytes of RGB triplets) from a credited source, embedded as a bytes literal in an image-format module. Palette tables are binary data by nature, and this is not executed or decoded into code.

  21. info warn → info by review encoding-hex src/PIL/WalImageFile.py:84: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.95 Part of the default palette byte table in WalImageFile.
    Reasoning

    Continuation of the same palette byte literal; monotone-ish RGB values consistent with palette data. Raw-pass hex-escape flag is a false positive on legitimate binary constant data.

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

    5 rows of escape runs, lines 90-96 (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.95 More rows of the default palette byte table.
    Reasoning

    Additional rows of the same palette constant, with no executable or decoding context. Hex-escape density is expected for binary tables.

  23. info warn → info by review encoding-hex src/PIL/WalImageFile.py:100: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.95 Further rows of the default palette byte table.
    Reasoning

    Same palette constant continuing; the byte values are plausible colour data and the code only indexes into it.

  24. info warn → info by review encoding-hex src/PIL/WalImageFile.py:102: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.95 Further rows of the default palette byte table.
    Reasoning

    Same palette constant, part of the 768-byte table; legitimate static data.

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

    8 rows of escape runs, lines 108-120 (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.95 Further rows of the default palette byte table.
    Reasoning

    Continuation of the palette constant with no code-execution context; benign binary table.

  26. info warn → info by review encoding-hex src/PIL/WalImageFile.py:127: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.95 Final rows of the default palette byte table.
    Reasoning

    Tail of the palette constant, closing the bytes literal. Static data, no decoding into code.

  27. info warn → info by review encoding-hex src/PIL/WalImageFile.py:128: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.95 Last row of the default palette byte table.
    Reasoning

    Final line of the same palette literal; benign static colour data.

  28. info critical → info by review dynamic-import src/PIL/features.py:38: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.85 Optional-module probe via __import__ in Pillow's features check.
    Reasoning

    Pillow's feature-detection code uses __import__ on a module name to probe whether optional codec/library modules are installed; the module name comes from internal constants, not untrusted input. Legitimate pattern, though dynamic import is worth noting.

  29. info critical → info by review dynamic-import src/PIL/features.py:59: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.85 Dynamic import to read a dependency version attribute.
    Reasoning

    Same feature-probing pattern: __import__ with fromlist to fetch a version attribute from an optional dependency. Module names are internal constants in Pillow.

  30. info critical → info by review dynamic-import src/PIL/features.py:149: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.85 Dynamic import of optional feature module to check a flag.
    Reasoning

    Imports a module named by a flag-to-module mapping within Pillow's feature check and reads an attribute; consistent with legitimate optional-feature detection.

  31. info critical → info by review dynamic-import src/PIL/features.py:172: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.85 Dynamic import to fetch a library version.
    Reasoning

    Duplicate of the version-probe pattern in features.py; internal module names only, standard for Pillow.

  32. info warn → info by review dynamic-attribute src/Tk/tkImaging.c:390: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.80 dlsym resolves Tk/Tcl function pointers at runtime.
    Reasoning

    Dynamic symbol lookup via dlsym in Pillow's Tk integration loads Tcl/Tk functions from a loaded library; the names are internal constants. Legitimate, though dlsym is a dynamic-resolution pattern.

  33. info warn → info by review dynamic-import src/Tk/tkImaging.c:439: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.80 dlopen of the main program handle to find Tk symbols.
    Reasoning

    dlopen(NULL) to search the main program's symbols first is a standard idiom for locating already-loaded Tk symbols; no untrusted path involved.

  34. info warn → info by review dynamic-import src/Tk/tkImaging.c:465: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.80 dlopen of the tkinter library for ImageTk bindings.
    Reasoning

    Loads the tkinter shared library by a computed name to bind Tk functions; standard for Pillow's ImageTk. Path derived from installed Tk, not attacker input.

  35. info warn → info by review identifier-confusable-collision src/libImaging/Access.c:83: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.90 Mode-constant identifiers flagged for skeleton collision in accessor table.
    Reasoning

    The identifiers IMAGING_MODE_* and similar are visually confusable only due to the tokenizer's skeleton normalization (I/l/1, O/0 letters in ALL-CAPS macro names). Ordinary C macro naming; no homoglyph evidence in the snippet.

  36. info warn → info by review identifier-confusable-collision src/libImaging/Convert.c:1093: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.90 Mode constants flagged by confusable-skeleton heuristic.
    Reasoning

    Same IMAGING_MODE_ identifier collision from the skeleton heuristic; ordinary macro names with no non-ASCII homoglyphs shown.

  37. info warn → info by review identifier-confusable-collision src/libImaging/Draw.c:60: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.90 Short coordinate identifiers flagged as confusable.
    Reasoning

    Variables xo/yo collapsing from x0/y0 or similar: a false positive from the 0/O normalization of short coordinate names; plain ASCII drawing code.

  38. info warn → info by review identifier-confusable-collision src/libImaging/Draw.c:60: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.90 Coordinate identifier flagged by confusable-skeleton heuristic.
    Reasoning

    Same as the sibling finding: x0/y0-style coordinate names trip the digit/letter skeleton heuristic; no homoglyphs.

  39. info warn → info by review identifier-confusable-collision src/libImaging/Draw.c:106: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.90 Coordinate parameter flagged by confusable-skeleton heuristic.
    Reasoning

    Parameter names x0/y0 normalize to the same skeleton as other identifiers; false positive from digit-letter folding in plain ASCII code.

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

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

    static inline void
    line8(Imaging im, int x0, int y0, int x1, int y1, int ink) {
        int i, n, e;
        int dx, dy;
    dismissed LLM, confidence 0.90 Coordinate parameter flagged by confusable-skeleton heuristic.
    Reasoning

    Parameter y1/y0 style names collide under the digit-letter folding; no homoglyph characters present in snippet. False positive.

  41. info warn → info by review identifier-confusable-collision src/libImaging/Draw.c:1006: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.90 Short local identifiers in Pillow's Draw.c collide under a visual confusable skeleton.
    Reasoning

    Skeleton collision on short identifiers like cl/cr/c1 in a Pillow drawing routine is a typical false positive from the confusable heuristic; the snippet shows ordinary per-channel arrays.

  42. info warn → info by review identifier-confusable-collision src/libImaging/Draw.c:1296: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.90 Angle normalization helper with a confusable-name collision on its parameters.
    Reasoning

    normalize_angles takes float pointers and performs ordinary angle arithmetic; the collision of a and l-style names is a heuristic artifact with no malicious content.

  43. info warn → info by review identifier-confusable-collision src/libImaging/Fill.c:79: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.90 Imaging mode validation check flagged for mode-constant name similarity.
    Reasoning

    The skeleton collision is between IMAGING_MODE_ constants that differ only by a mode letter and standard capitalization; ordinary mode validation code.

  44. info warn → info by review identifier-confusable-collision src/libImaging/Geometry.c:770: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.90 Coordinate parameter names in Geometry.c collide under confusable skeleton matching.
    Reasoning

    Parameters x0/x1 and xo-style names are standard coordinate parameters in a geometry routine; the collision is a heuristic false positive.

  45. info warn → info by review identifier-confusable-collision src/libImaging/Geometry.c:771: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.90 Coordinate parameter names in Geometry.c flagged for visual similarity.
    Reasoning

    y0/y1 style coordinate parameters in a standard image geometry function; no obfuscation evident.

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

    `#define inline` rebinds the reserved C keyword `inline` (token pass, confidence 0.85)

    #if defined(_MSC_VER) && !defined(__GNUC__)
    #define inline __inline
    #endif
    #endif
    likely benign LLM, confidence 0.85 Platform header maps inline to __inline for MSVC compilers.
    Reasoning

    Defining inline as __inline under MSVC is a long-standing portability idiom in CPython-family headers, not malicious keyword abuse.

  47. info warn → info by review macro-keyword-override src/libImaging/ImPlatform.h:60: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.85 Macro alias naming float type as FLOAT32 for pixel-type definitions.
    Reasoning

    Mapping FLOAT32 to float is a standard typedef-like macro for fixed-width type naming in image code; no hidden payload or execution.

  48. info warn → info by review macro-keyword-override src/libImaging/ImPlatform.h:61: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.85 Macro alias naming double type as FLOAT64 for pixel-type definitions.
    Reasoning

    Same as FLOAT32: a benign type-name macro for the double type in a portable header.

  49. info warn → info by review identifier-confusable-collision src/libImaging/Mode.c:12: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"},
    dismissed LLM, confidence 0.90 Mode-name lookup table flagged for confusable identifier similarity.
    Reasoning

    Mode name table with IMAGING_MODE_ identifiers; the skeleton collision is from capitalization and the letter I/l, a heuristic false positive.

  50. info warn → info by review identifier-confusable-collision src/libImaging/Mode.c:54: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"},
    dismissed LLM, confidence 0.90 Raw mode name table flagged for confusable identifier similarity.
    Reasoning

    Rawmode name table of standard Pillow constants; no obfuscation evident.

  51. info warn → info by review identifier-confusable-collision src/libImaging/Mode.c:80: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"},
    dismissed LLM, confidence 0.90 Raw mode table with packing-format suffixes flagged for name collision.
    Reasoning

    Rawmode table entries such as 1;I and 1;8 differ by pack-format suffix; the collision is a heuristic artifact of the enum naming.

  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,
    dismissed LLM, confidence 0.90 Image mode enum flagged for confusable identifier skeleton.
    Reasoning

    Enum of standard image mode constants in a header; the collision is a naming artifact, not a threat.

  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,
    dismissed LLM, confidence 0.90 Raw mode enum aliases flagged for confusable identifier skeleton.
    Reasoning

    Rawmode enum alias block in a standard header; benign naming collision.

  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,
    dismissed LLM, confidence 0.93 Pillow raw-mode enum constants for 1-bit and 8-bit image layouts.
    Reasoning

    The identifiers IMAGING_RAWMODE_1_8, IMAGING_RAWMODE_1_I and IMAGING_RAWMODE_1_IR are ordinary Pillow raw-mode enum names. The 1/I and 8/O-style collision is an artifact of the confusable-skeleton heuristic, which treats 1, I and l as equivalent. No obfuscation or execution is present.

  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},
    dismissed LLM, confidence 0.93 Pack table entry for 1-bit image mode with raw-mode variants.
    Reasoning

    The table maps IMAGING_MODE_1 to IMAGING_RAWMODE_1 and to variants such as _I and _R with pack functions. The skeleton collision comes from 1 versus I in the naming scheme. This is normal image-packing dispatch code.

  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},
    dismissed LLM, confidence 0.93 Pack table entry for the RAWMODE_1 variants.
    Reasoning

    Same table as the previous finding, with the same legitimate 1-bit raw-mode naming. The confusable match is a heuristic false positive from 1/I/l overlap in Pillow's identifiers.

  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;
    dismissed LLM, confidence 0.94 Storage setup branch for 1-bit image mode.
    Reasoning

    A plain comparison of mode against IMAGING_MODE_1 followed by setting bands and pixelsize to 1 for 1-bit images. Standard image-storage logic with no suspicious behavior.

  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 */
    dismissed LLM, confidence 0.94 Static unpack function for 1-byte-per-pixel boolean image data.
    Reasoning

    The function name unpack18 and its comment describing a numpy boolean ('|b1') unpacking are legitimate Pillow code. The skeleton collision is a false match on the digits 1 and 8 in the name.

  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},
    dismissed LLM, confidence 0.93 Unpack table entry for 1-bit image mode.
    Reasoning

    Unpack dispatch table with the same legitimate 1-bit mode naming as the Pack.c entries. The collision is a confusable-heuristic false positive.

  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},
    dismissed LLM, confidence 0.93 Unpack table entry for RAWMODE_1 variants.
    Reasoning

    Unpack table entry for the RAWMODE_1 variants, consistent with the rest of the file. No dynamic execution or encoded payload is involved.

  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},
    
        /* grayscale */
    dismissed LLM, confidence 0.93 Unpack table entry for additional 1-bit raw-mode variants.
    Reasoning

    Unpack table entry for the RAWMODE_1_IR and RAWMODE_1_8 variants, which are ordinary Pillow mode names. The collision is a heuristic false positive from character confusability.

Reproduce This Scan

Run disclude locally to verify. 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.0.0
git clone https://github.com/python-pillow/Pillow Pillow && cd Pillow
git checkout 12.3.0
disclude scan --severity info --diff 12.2.0 .