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
1662 scanned, 2826 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 35 info
Scan time
23.6 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 30 of 30 warn and critical signals; 30 demoted to info.
Disclude version
4.0.0
Scanned (UTC)
2026-10-14 18:46

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.90 Long inheritance-diagram directive listing matplotlib class names in API docs.
    Reasoning

    A very long line in an RST doc containing a list of class names for an inheritance-diagram directive. Typical of generated or auto-listed documentation; no malicious content.

  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 Sphinx configuration probing optional dependencies via __import__ in a try/except.
    Reasoning

    Sphinx conf checking for optional modules by importing names in a loop, catching ImportError and recording missing ones. The dynamic __import__ is on a module name from a known list, a common pattern for detecting missing optional dependencies. Still a non-literal import, so a small doubt remains.

  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)
    dismissed LLM, confidence 0.90 Unicode demo list of bar labels in a release-notes doc.
    Reasoning

    Greek alpha used as a label string in a documentation example showing Unicode text labels for plots. It appears inside a list of deliberately diverse Unicode demo strings, so the homoglyph flag is a false positive in prose/example code.

  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
    dismissed LLM, confidence 0.90 Russian-language plot title in a release-notes example.
    Reasoning

    Cyrillic text 'со смайликами' is a Russian title string in a documentation example demonstrating Unicode rendering. Intentional localized text, not an identifier-spoofing attack.

  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.85 Vendored AGG slider control using sprintf with a stored format string.
    Reasoning

    This is the vendored AGG library's slider control, which calls sprintf with a format string member. The format is a developer-set label, not attacker input, but the pattern is a genuine format-string risk in the upstream library. Not obfuscation or 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.90 Gallery example evaluating locator expression strings for date tick formatting.
    Reasoning

    Example script that evaluates a user-specified locator string from a demo configuration list. eval here is used on developer-authored example strings for matplotlib locators; it is a demo pattern, not a dropper or hidden 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.90 Gallery example evaluating formatter expression strings for date ticks.
    Reasoning

    Same example file; eval on formatter string from the developer's demo list to show tick formatter options. 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 Textbox widget example evaluating a typed expression to plot data.
    Reasoning

    Widget example that evaluates a user-typed math expression with restricted globals ({'np': np}) to plot a function. Interactive demo; the eval is a known and intentional part of the example, not hidden malicious behavior, though eval on user input is risky in general.

  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
    dismissed LLM, confidence 0.90 Documentation snippet illustrating eval in an interactive guide.
    Reasoning

    Prose documentation showing an eval call inside a markup code block illustrating an interactive REPL loop. The eval is on a string literal context in docs, 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.90 Deprecation helper walking frames to find the caller's stacklevel.
    Reasoning

    Matplotlib's API helper uses sys._getframe to walk the call stack and determine the caller's stacklevel for deprecation warnings. Standard, legitimate introspection.

  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.90 Axes code using inspect.stack to name the caller in a kwarg error.
    Reasoning

    Uses inspect.stack()[1].function to report the calling function name in a keyword-argument error. Ordinary error-message construction.

  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.
    Reasoning

    Minified vendored JavaScript (ResizeObserver polyfill from jsxtools) on a single long line. Minification explains the length; the visible code is a standard polyfill with no suspicious network or exec behavior in the snippet.

  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.95 Collects keyword parameter values from locals() in matplotlib's layout engine.
    Reasoning

    Reading locals() by parameter name inside a method that forwards keyword arguments is a common Python idiom; the names come from the function's own __kwdefaults__, so this is ordinary introspection, not obfuscation.

  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.95 Assigns parameter values read from locals() into the engine's _params dict.
    Reasoning

    Same locals()-by-name parameter forwarding as the sibling finding; it reads the function's own argument names with no external input.

  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.95 Repeats the locals()-by-name parameter collection in a second layout engine class.
    Reasoning

    Duplicate of the same locals()-based parameter forwarding pattern in another constructor/method; 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 locals()-derived parameter values into _params in the second layout engine class.
    Reasoning

    Same benign locals() forwarding pattern; the keys are the function's own keyword names.

  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.93 Copies backend function signatures onto pyplot module-level wrappers via globals().
    Reasoning

    Iterates a hardcoded list of backend function names and sets __signature__ on module globals; a known matplotlib pattern for exposing backend signatures, with no external data involved.

  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.90 Checks whether a plot directive's code block is valid Python via compile().
    Reasoning

    compile() on documentation source text purely to check syntax validity is a normal Sphinx directive check; it does not execute 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 Executes the configured plot_pre_code setup string in the plot directive namespace.
    Reasoning

    exec of plot_pre_code from the Sphinx configuration is the documented way to run setup code for doc plots; this is intended build-time execution of the project's own docs code, not hidden malice.

  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.85 Runs the documented plot code block with exec inside a namespace.
    Reasoning

    Executing user plot code from documentation sources is the purpose of the plot directive. It runs at docs build time, which is expected behavior for this tool, though it is arbitrary code execution by design.

  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 Invokes a named plotting function from the directive option via exec on a constructed call string.
    Reasoning

    exec of function_name + '()' looks like a constructed string, but function_name comes from the directive's own parsed options and the call is a simple invocation of a plot function. Flagged critical by the signal, yet the context is the documented plot directive behavior.

  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.90 Decodes base64 test data into vertex arrays for a path simplification test.
    Reasoning

    base64 decoding of an embedded test vertex array followed by numpy frombuffer is ordinary test fixture data handling; no decompression to executable code, just path vertices.

  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.90 Evaluates a Bbox repr in a test to verify round-tripping.
    Reasoning

    eval of repr(b) with a restricted namespace in a test that round-trips a Bbox; the input is the test's own object, a standard test idiom.

  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.90 Evaluates a Bbox string form in a test to build a dict for comparison.
    Reasoning

    eval of str(b) in a test with a restricted namespace, operating on a locally created Bbox; benign test code.

  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.90 Evaluates formatted Bbox attribute values in a test assertion.
    Reasoning

    eval of a format string applied to test-local values in a test assertion; no external input, benign.

  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.85 A test evaluates a formatted attribute string to check it round-trips to the expected value.
    Reasoning

    The eval runs on a format string built from the test's own asdict values and fixed format specs, inside a unit test. It is a round-trip check, not attacker-controlled input, though eval is a poor pattern.

  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 variable xo holds an x offset for an affine transform.
    Reasoning

    The snippet is ordinary geometry code: reading offsets and applying an affine transform. The confusable match comes from short names like xo and a near-identical identifier, which is a naming heuristic false positive. Token-pass findings of this kind are noisy.

  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 variable yo holds a y offset for an affine transform.
    Reasoning

    Same pattern as the xo finding: yo is the y offset used with offset_trans.transform, which is standard rendering code. The collision is a short-name heuristic artifact.

  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.90 Opens the main program handle to look up already-loaded Tcl/Tk symbols.
    Reasoning

    dlopen(nullptr, RTLD_LAZY) is the standard idiom for obtaining a handle to the main program's global symbols. It is used to find Tcl/Tk symbols already linked into the process, and the nullptr argument is not a path being 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.80 Loads the Tcl/Tk shared library from a path resolved on the Python side.
    Reasoning

    The path comes from py_path_b, a path supplied by the Python side that locates the tkinter shared library. Loading a library from a runtime-determined path is a legitimate need for this feature, and the code reports errors via dlerror. Risk exists only if the path is attacker-controlled, which the snippet does not suggest.

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/matplotlib/matplotlib matplotlib && cd matplotlib
git checkout v3.11.2
disclude scan --severity info --diff v3.11.1 .