PyPI / matplotlib / 3.11.2

matplotlib 3.11.2

Source
https://github.com/matplotlib/matplotlib
Tag scanned
v3.11.2
Compared with
v3.11.1 (0 signals introduced since)
Files
1673 scanned, 2815 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 35 info
Scan time
26.3 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 30 of 30 warn and critical signals; 30 demoted to info.
Disclude version
4.1.0
Scanned (UTC)
2026-10-22 05:09

Signals

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

  1. info warn → info by review long-line doc/api/artist_api.rst:14:1

    line length 2658 characters (raw pass, confidence 0.65)

    .. inheritance-diagram:: matplotlib.axes._axes.Axes matplotlib.axes._base._AxesBase matplotlib.axis.Axis matplotlib.axis.Tick matplotlib.axis.XAxis matplotlib.axis.XTick matplotlib.axis.YAxis matplotlib.axis.YTick matplotlib.collections.AsteriskPolygonCollection matplotlib.collections.CircleCollection matplotlib.collec
    likely benign LLM, confidence 0.97 Sphinx inheritance-diagram directive with a very long list of class names.
    Reasoning

    A long line in an RST doc containing an inheritance-diagram directive with a long list of class names. Purely formatting, no code execution or obfuscation.

  2. info critical → info by review dynamic-import doc/conf.py:163:13

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

            try:
                __import__(name)
            except ImportError:
                missing.append(names[name])
    likely benign LLM, confidence 0.85 Import-availability check in the Sphinx config that uses __import__ on module names from a list.
    Reasoning

    Sphinx conf.py checking whether optional modules are importable, appending missing ones to a list. The name comes from a fixed list of module names in the project's own config, so this is a standard availability check and not malicious.

  3. info warn → info by review unicode-homoglyph doc/release/prev_whats_new/dflt_style_changes.rst:707:25

    identifier `α` contains homoglyph candidates: α (03B1)→a (raw pass, confidence 0.70)

       tick_labels = ['😃', '😎', '😴', '😲', '😻']
       bar_labels = ['א', 'α', '☣', '⌬', 'ℝ']
       y = [1, 4, 9, 16, 25]
       x = range(5)
    likely benign LLM, confidence 0.93 Unicode demo labels in an archived what's-new doc that include a Greek letter.
    Reasoning

    Greek alpha appears in a sample list of Unicode labels in release-notes documentation demonstrating Unicode text rendering. It is a demo string in an illustrative example, not an identifier used for deception.

  4. info warn → info by review unicode-homoglyph doc/release/prev_whats_new/dflt_style_changes.rst:717:37

    identifier `со` contains homoglyph candidates: с (0441)→c, о (043E)→o (raw pass, confidence 0.70)

    
       ax.set_title('Диаграмма со смайликами')
    
    See the `DejaVu Sans PDF sample for full coverage
    likely benign LLM, confidence 0.93 Russian title text in a docs example string.
    Reasoning

    Cyrillic text inside a string literal ('Диаграмма со смайликами') in a documentation example showing Unicode title rendering. This is string content, not a homoglyph identifier used to hide code.

  5. line length 1249 characters (raw pass, confidence 0.50)

            <img alt="viridis colormap" title="viridis" style="border: 1px solid #555;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAABACAYAAABsv8+/AAAAFnRFWHRUaXRsZQB2aXJpZGlzIGNvbG9ybWFwrE0mCwAAABx0RVh0RGVzY3JpcHRpb24AdmlyaWRpcyBjb2xvcm1hcAtjl3IAAABKdEVYdEF1dGhvcgBNYXRwbG90bGliIHYzLjQuMHJjMy5wb3N0OS5kZXYwK2czZ
  6. info warn → info by review dynamic-format-string extern/agg24-svn/src/ctrl/agg_slider_ctrl.cpp:165:17

    `sprintf` called with a non-literal format string (format-string bug pattern) (ast pass, confidence 0.80)

                    char buf[256];
                    sprintf(buf, m_label, value());
                    m_text.text(buf);
                }
    likely benign LLM, confidence 0.80 Vendored AGG slider calling sprintf with a non-literal format string.
    Reasoning

    Vendored AGG library slider control calls sprintf with a label format from a member variable. This is a real format-string bug pattern in the third-party code, but the format comes from internal widget configuration, not attacker input, so it is a code-quality issue rather than malicious intent.

  7. info warn → info by review dynamic-execution galleries/examples/ticks/date_formatters_locators.py:32:36

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

        if locator:
            ax.xaxis.set_major_locator(eval(locator))
            ax.xaxis.set_major_formatter(DateFormatter(fmt))
        else:
    likely benign LLM, confidence 0.85 Gallery example evaluating locator constructor strings with eval.
    Reasoning

    Example script that eval()s a locator string taken from a demo configuration list of trusted, hardcoded strings. It is a teaching example with a known bad practice but no malicious payload.

  8. info warn → info by review dynamic-execution galleries/examples/ticks/date_formatters_locators.py:35:38

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

        else:
            ax.xaxis.set_major_formatter(eval(formatter))
        ax.text(0.0, 0.2, locator or formatter, transform=ax.transAxes,
                fontsize=14, fontname='Monospace', color='tab:blue')
    likely benign LLM, confidence 0.85 Gallery example evaluating formatter strings with eval.
    Reasoning

    Same gallery example evaluating formatter strings from hardcoded demo values with eval. Benign teaching code.

  9. info warn → info by review dynamic-execution galleries/examples/widgets/textbox.py:35:13

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

        """
        ydata = eval(expression, {'np': np}, {'t': t})
        l.set_ydata(ydata)
        ax.relim()
    likely benign LLM, confidence 0.85 Widget example that evaluates a typed expression with eval and restricted namespaces.
    Reasoning

    Textbox widget example that evaluates a user-typed math expression with restricted globals (only np and t). It is an interactive demo; eval is unsafe in general but the code is a standard example, not a dropper.

  10. identifier `αб` mixes scripts: Greek + Cyrillic (in string/comment) (raw pass, confidence 0.40)

    axd = plt.figure(layout="constrained").subplot_mosaic(
        """αб
           ℝ☢"""
    )
  11. info warn → info by review dynamic-execution galleries/users_explain/figure/interactive_guide.rst:443:12

    [markup prose] `eval` called on a string literal (ast pass, confidence 0.85)

               eval(inp)                  # Evaluate / Print
    likely benign LLM, confidence 0.90 Documentation prose illustrating an eval call in an interactive loop.
    Reasoning

    Prose in a documentation file showing an interactive REPL idiom 'eval(inp) # Evaluate / Print'. It is markup text in an explanatory doc, not executed code.

  12. info warn → info by review frame-introspection lib/matplotlib/_api/__init__.py:480:17

    `sys._getframe` — call-stack frame introspection (ast pass, confidence 0.70)

        else:
            frame = sys._getframe()
            for stacklevel in itertools.count(1):
                if frame is None:
    likely benign LLM, confidence 0.95 Stack-frame walk to locate the caller for warnings.
    Reasoning

    Matplotlib's API helper uses sys._getframe to find caller stack level for deprecation warnings. This is a standard, legitimate introspection pattern.

  13. info warn → info by review frame-introspection lib/matplotlib/axes/_base.py:266:40

    `inspect.stack` — call-stack frame introspection (ast pass, confidence 0.70)

                if pos_only in kwargs:
                    raise _api.kwarg_error(inspect.stack()[1].function, pos_only)
    
            if not args:
    likely benign LLM, confidence 0.95 inspect.stack used to name the calling function in an error message.
    Reasoning

    Uses inspect.stack()[1].function to build a keyword-argument error message. Legitimate use for error reporting.

  14. info warn → info by review long-line lib/matplotlib/backends/web_backend/js/mpl.js:732:1

    line length 2004 characters (raw pass, confidence 0.65)

    var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError("Constructor requires 'new' operator");i.set(this,e)}function h(){throw new TypeError("Function is not a constructor")}function c(e,t,i,n){e=0 in ar
    likely benign LLM, confidence 0.85 Minified vendored ResizeObserver polyfill in the web backend JavaScript.
    Reasoning

    A minified, bundled ResizeObserver polyfill (_JSXTOOLS_RESIZE_OBSERVER) shipped in a web backend. Minification produces long lines and is expected for vendored browser libraries; the content looks like a standard polyfill.

  15. info warn → info by review dynamic-attribute lib/matplotlib/layout_engine.py:212:16

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

            for td in self.set.__kwdefaults__:
                if locals()[td] is not None:
                    self._params[td] = locals()[td]
    likely benign LLM, confidence 0.92 Collects constructor keyword arguments by name using locals().
    Reasoning

    Reads keyword defaults by name via locals() inside a constructor to collect parameters. The keys come from the function's own __kwdefaults__, so this is a legitimate, if unusual, pattern.

  16. info warn → info by review dynamic-attribute lib/matplotlib/layout_engine.py:213:36

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

                if locals()[td] is not None:
                    self._params[td] = locals()[td]
    likely benign LLM, confidence 0.92 Second locals() lookup in the same parameter-collection loop.
    Reasoning

    Second use of locals() in the same parameter-collection pattern, reading the function's own arguments. Benign.

  17. info warn → info by review dynamic-attribute lib/matplotlib/layout_engine.py:308:16

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

            for td in self.set.__kwdefaults__:
                if locals()[td] is not None:
                    self._params[td] = locals()[td]
    likely benign LLM, confidence 0.92 Repeated locals()-based parameter collection in another layout engine.
    Reasoning

    Same locals()-based parameter collection pattern repeated in another layout engine class. Benign.

  18. info warn → info by review dynamic-attribute lib/matplotlib/layout_engine.py:309:36

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

                if locals()[td] is not None:
                    self._params[td] = locals()[td]
    likely benign LLM, confidence 0.95 Copies named constructor parameters from locals() into self._params.
    Reasoning

    locals() lookup by name is a common Python idiom for copying keyword arguments into an attribute dict. No external input or hidden behavior is involved.

  19. info unicode-homoglyph lib/matplotlib/path.py:1131:36

    identifier `α` contains homoglyph candidates: α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

        method as for collections: each is iterated over independently, so if you have 3
        paths (A, B, C), 2 transforms (α, β) and 1 offset (O), their combinations are as
        follows:
  20. info unicode-homoglyph lib/matplotlib/path.py:1134:11

    identifier `α` contains homoglyph candidates: α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

    
        - (A, α, O)
        - (B, β, O)
        - (C, α, O)
  21. info unicode-homoglyph lib/matplotlib/path.py:1136:11

    identifier `α` contains homoglyph candidates: α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

        - (B, β, O)
        - (C, α, O)
        """
        from .transforms import Bbox
  22. info warn → info by review dynamic-attribute lib/matplotlib/pyplot.py:535:9

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

        for func_name in ["new_figure_manager", "draw_if_interactive", "show"]:
            globals()[func_name].__signature__ = inspect.signature(
                getattr(backend_mod, func_name))
    likely benign LLM, confidence 0.95 Assigns backend function signatures to pyplot's module-level functions via globals().
    Reasoning

    Iterates over a fixed list of module function names and sets __signature__ on them via globals(). This is a plain metaprogramming pattern for matching backend signatures.

  23. info warn → info by review dynamic-execution lib/matplotlib/sphinxext/plot_directive.py:368:9

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

            # check if it's valid Python as-is
            compile(text, '<string>', 'exec')
            return False
        except SyntaxError:
    likely benign LLM, confidence 0.93 Syntax-checks a docs code block with compile() without executing it.
    Reasoning

    compile() with mode 'exec' is used only to check whether a documentation code block is valid Python, and it returns a bool. It never runs the code.

  24. info warn → info by review dynamic-execution lib/matplotlib/sphinxext/plot_directive.py:588:21

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

                    else:
                        exec(str(setup.config.plot_pre_code), ns)
                if "__main__" in code:
                    ns['__name__'] = '__main__'
    likely benign LLM, confidence 0.85 Runs the project's configured plot_pre_code inside the docs build namespace.
    Reasoning

    The plot directive is a Sphinx extension whose job is to execute example code during docs builds. The pre-code comes from the project's own configuration, so executing it is expected, not an attack.

  25. info warn → info by review dynamic-execution lib/matplotlib/sphinxext/plot_directive.py:594:17

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

                with cbook._setattr_cm(FigureManagerBase, show=lambda self: None):
                    exec(code, ns)
                    if function_name is not None:
                        exec(function_name + "()", ns)
    likely benign LLM, confidence 0.88 Executes documentation example code to generate plot images.
    Reasoning

    Executing example code from documentation sources is the documented purpose of the plot directive. The show() call is stubbed during execution, which is consistent with a docs build.

  26. info critical → info by review dynamic-execution lib/matplotlib/sphinxext/plot_directive.py:596:21

    `exec` called on a constructed string (ast pass, confidence 0.80)

                    if function_name is not None:
                        exec(function_name + "()", ns)
    
            except (Exception, SystemExit) as err:
    likely benign LLM, confidence 0.80 Calls a named example function inside the docs build namespace.
    Reasoning

    function_name comes from the directive's own parsing of example source, and the constructed call is just name plus '()'. The critical severity overstates the risk. The code runs only in the trusted docs-build context.

  27. info warn → info by review decoder-decompress-payload lib/matplotlib/tests/test_simplification.py:449:27

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

    
        verts = np.frombuffer(base64.decodebytes(data), dtype='<i4')
        verts = verts.reshape((len(verts) // 2, 2))
        path = Path(verts)
    likely benign LLM, confidence 0.85 Decodes base64 test vertex data into a numpy array for a path simplification test.
    Reasoning

    The base64 blob decodes to int32 vertex coordinates for a path test. It is test fixture data, not a dropper, and no decompressed content is executed.

  28. info warn → info by review dynamic-execution lib/matplotlib/tests/test_transforms.py:879:23

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

        b = mtransforms.Bbox([[.5, 0], [.75, .75]])
        assert_bbox_eq(b, eval(repr(b), {'Bbox': mtransforms.Bbox}))
        asdict = eval(str(b), {'Bbox': dict})
        for k, v in asdict.items():
    likely benign LLM, confidence 0.93 Round-trip evaluates a Bbox repr in a test.
    Reasoning

    eval of repr() of a Bbox with a restricted namespace is a round-trip test of the repr. The input is the object's own output, not external data.

  29. info warn → info by review dynamic-execution lib/matplotlib/tests/test_transforms.py:880:14

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

        assert_bbox_eq(b, eval(repr(b), {'Bbox': mtransforms.Bbox}))
        asdict = eval(str(b), {'Bbox': dict})
        for k, v in asdict.items():
            assert getattr(b, k) == v
    likely benign LLM, confidence 0.93 Evaluates a Bbox string form in a test to check formatting.
    Reasoning

    eval of str() of a Bbox with a restricted namespace checks string formatting in a test. The input comes from the test's own object.

  30. info warn → info by review dynamic-execution lib/matplotlib/tests/test_transforms.py:884:14

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

        fmt = '.1f'
        asdict = eval(format(b, fmt), {'Bbox': dict})
        for k, v in asdict.items():
            assert eval(format(getattr(b, k), fmt)) == v
    likely benign LLM, confidence 0.93 Evaluates a formatted Bbox string in a test.
    Reasoning

    eval of format() output with a format spec in a test checks Bbox formatting. The inputs are test-controlled constants.

  31. info warn → info by review dynamic-execution lib/matplotlib/tests/test_transforms.py:886:16

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

        for k, v in asdict.items():
            assert eval(format(getattr(b, k), fmt)) == v
    likely benign LLM, confidence 0.93 Evaluates formatted attribute values in a test assertion.
    Reasoning

    Evaluates formatted numeric attribute strings in a test loop to check values. The data is controlled by the test itself.

  32. info warn → info by review identifier-confusable-collision src/_path.h:395:20

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

            if (Noffsets) {
                double xo = offsets(i % Noffsets, 0);
                double yo = offsets(i % Noffsets, 1);
                offset_trans.transform(&xo, &yo);
    dismissed LLM, confidence 0.80 Local double variable xo holding an offset x coordinate.
    Reasoning

    xo and yo are ordinary short coordinate variable names in a C++ loop. The confusability flag comes from a skeleton collision on short names and has no security meaning.

  33. info warn → info by review identifier-confusable-collision src/_path.h:396:20

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

                double xo = offsets(i % Noffsets, 0);
                double yo = offsets(i % Noffsets, 1);
                offset_trans.transform(&xo, &yo);
                trans *= agg::trans_affine_translation(xo, yo);
    dismissed LLM, confidence 0.80 Local double variable yo holding an offset y coordinate.
    Reasoning

    yo is an ordinary short coordinate variable beside xo in a C++ loop. The skeleton collision is noise from short identifiers.

  34. info warn → info by review dynamic-import src/_tkagg.cpp:315:25

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

        // Try loading from the main program namespace first.
        auto main_program = dlopen(nullptr, RTLD_LAZY);
        auto success = load_tcl_tk(main_program);
        // We don't need to keep a reference open as the main program always exists.
    likely benign LLM, confidence 0.85 Opens the main program handle with dlopen(nullptr) to locate Tcl/Tk symbols.
    Reasoning

    dlopen(nullptr) opens the main program's symbol namespace to find already-loaded Tcl/Tk. This is standard practice for embedding Tk in a Python extension, and the path is not attacker-controlled.

  35. info warn → info by review dynamic-import src/_tkagg.cpp:336:24

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

        std::string path = py_path_b;
        auto tkinter_lib = dlopen(path.c_str(), RTLD_LAZY);
        if (!tkinter_lib) {
            throw std::runtime_error(dlerror());
    likely benign LLM, confidence 0.85 Loads the tkinter shared library from a Python-provided path.
    Reasoning

    The path comes from Python's tkinter module location, which the extension resolves to load the matching Tk library. This is expected plugin loading, not an arbitrary payload fetch.

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.1.0
git clone https://github.com/matplotlib/matplotlib matplotlib && cd matplotlib
git checkout v3.11.2
disclude scan --severity info --diff v3.11.1 .