PyPI / scikit-learn / 1.9.1

scikit-learn 1.9.1

Source
https://github.com/scikit-learn/scikit-learn
Tag scanned
1.9.1
Compared with
1.9.0 (8 signals introduced since)
Files
1300 scanned, 377 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 20 info
Scan time
18.6 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 19 of 19 warn and critical signals; 19 demoted to info.
Disclude version
4.1.0
Scanned (UTC)
2026-10-22 04:03

Signals

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

  1. info critical → info by review dynamic-import sklearn/externals/array_api_compat/cupy/__init__.py:12:1 new in this release

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

    # See the comment in the numpy __init__.py
    __import__(__spec__.parent + '.linalg')
    __import__(__spec__.parent + '.fft')
    likely benign LLM, confidence 0.93 Imports own linalg and fft submodules dynamically in vendored compat layer.
    Reasoning

    Vendored array_api_compat uses __import__ on its own package parent plus fixed suffixes to load linalg/fft submodules so it can be vendored; the argument is not external input.

  2. info critical → info by review dynamic-import sklearn/externals/array_api_compat/cupy/__init__.py:13:1 new in this release

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

    __import__(__spec__.parent + '.linalg')
    __import__(__spec__.parent + '.fft')
    
    __array_api_version__: Final = '2025.12'
    likely benign LLM, confidence 0.93 Imports own linalg and fft submodules dynamically in vendored compat layer.
    Reasoning

    Same vendored submodule import of linalg/fft relative to __spec__.parent; standard for vendored array_api_compat.

  3. info critical → info by review dynamic-import sklearn/externals/array_api_compat/dask/array/__init__.py:16:1 new in this release

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

    # See the comment in the numpy __init__.py
    __import__(__spec__.parent + '.linalg')
    __import__(__spec__.parent + '.fft')
    likely benign LLM, confidence 0.93 Imports own linalg and fft submodules dynamically in vendored dask compat layer.
    Reasoning

    Vendored array_api_compat submodule loading via __spec__.parent with fixed suffixes; benign.

  4. info critical → info by review dynamic-import sklearn/externals/array_api_compat/dask/array/__init__.py:17:1 new in this release

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

    __import__(__spec__.parent + '.linalg')
    __import__(__spec__.parent + '.fft')
    
    __all__ = sorted(
    likely benign LLM, confidence 0.93 Imports own linalg and fft submodules dynamically in vendored dask compat layer.
    Reasoning

    Same vendored submodule loading pattern as the other compat modules; benign.

  5. info critical → info by review dynamic-import sklearn/externals/array_api_compat/numpy/__init__.py:22:1 new in this release

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

    # dynamically so that the library can be vendored.
    __import__(__spec__.parent + ".linalg")
    
    __import__(__spec__.parent + ".fft")
    likely benign LLM, confidence 0.93 Imports own linalg submodule dynamically in vendored numpy compat layer.
    Reasoning

    Vendored array_api_compat numpy module imports its own linalg submodule relative to package parent; standard vendoring technique.

  6. info critical → info by review dynamic-import sklearn/externals/array_api_compat/numpy/__init__.py:24:1 new in this release

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

    
    __import__(__spec__.parent + ".fft")
    
    from .linalg import matrix_transpose, vecdot  # type: ignore[no-redef]  # noqa: F401
    likely benign LLM, confidence 0.93 Imports own fft submodule dynamically in vendored numpy compat layer.
    Reasoning

    Imports own fft submodule relative to package parent in vendored compat code; benign.

  7. info critical → info by review dynamic-import sklearn/externals/array_api_compat/torch/__init__.py:13:1 new in this release

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

    # See the comment in the numpy __init__.py
    __import__(__spec__.parent + '.linalg')
    __import__(__spec__.parent + '.fft')
    likely benign LLM, confidence 0.93 Imports own linalg and fft submodules dynamically in vendored torch compat layer.
    Reasoning

    Vendored torch compat layer loads linalg/fft submodules relative to its own package; benign.

  8. info critical → info by review dynamic-import sklearn/externals/array_api_compat/torch/__init__.py:14:1 new in this release

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

    __import__(__spec__.parent + '.linalg')
    __import__(__spec__.parent + '.fft')
    
    __array_api_version__: Final = '2025.12'
    likely benign LLM, confidence 0.93 Imports own linalg and fft submodules dynamically in vendored torch compat layer.
    Reasoning

    Same vendored submodule loading in torch compat layer; benign.

  9. info warn → info by review dynamic-import build_tools/circle/build_doc.sh:175:1

    `source` called with a dynamic path — sourcing a variable script path (ast pass, confidence 0.75)

    bash ./miniconda.sh -b -p $MINIFORGE_PATH
    source $MINIFORGE_PATH/etc/profile.d/conda.sh
    conda activate
    likely benign LLM, confidence 0.90 Sources conda activation script from a variable miniconda install path in CI.
    Reasoning

    Sourcing conda's profile script from a variable install path set earlier in the CI script is standard Miniforge/conda setup; the path is a local install directory, not attacker-controlled input.

  10. info warn → info by review dynamic-import build_tools/github/create_gpu_environment.sh:8:1

    `source` called with a dynamic path — sourcing a variable script path (ast pass, confidence 0.75)

    bash Miniforge3-$(uname)-$(uname -m).sh -b -p "${HOME}/conda"
    source "${HOME}/conda/etc/profile.d/conda.sh"
    likely benign LLM, confidence 0.90 Sources conda.sh after installing Miniforge into a home directory path.
    Reasoning

    Standard Miniforge installer followed by sourcing conda.sh from $HOME/conda; routine CI environment setup with no obfuscation.

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

    pushd $TEST_DIR
    $CMD -m sklearn.utils.tests.test_estimator_checks
    popd
  12. info critical → info by review dynamic-execution build_tools/github/test_script.sh:67:1

    `eval` called on a dynamic (variable/substitution) expression (ast pass, confidence 0.90)

    set -x
    eval "$TEST_CMD"
    set +x
    likely benign LLM, confidence 0.70 Evaluates a test command string from CI environment variable with tracing.
    Reasoning

    eval of $TEST_CMD is a risky pattern but in a CI test script the command comes from the project's own CI configuration, not untrusted input; it is a common test-runner idiom with set -x for logging.

  13. info warn → info by review dynamic-import build_tools/shared.sh:52:9

    `source` called with a dynamic path — sourcing a variable script path (ast pass, confidence 0.75)

        elif [[ "$DISTRIB" == "ubuntu" || "$DISTRIB" == "debian-32" ]]; then
            source $VIRTUALENV/bin/activate
        fi
    }
    likely benign LLM, confidence 0.85 Sources virtualenv activate script from a configured path in CI helper.
    Reasoning

    Sources a virtualenv activate script from a variable VIRTUALENV path inside a CI distro-specific branch; ordinary build helper behavior.

  14. info critical → info by review dynamic-import doc/sphinxext/github_link.py:43:14

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

        class_name = info["fullname"].split(".")[0]
        module = __import__(info["module"], fromlist=[class_name])
        obj = attrgetter(info["fullname"])(module)
    likely benign LLM, confidence 0.92 Dynamically imports documented module to locate source for GitHub links.
    Reasoning

    Sphinx extension importing the module named in its own object info to resolve source links; the module name comes from documentation introspection, a legitimate pattern.

  15. info critical → info by review dynamic-import doc/sphinxext/github_link.py:62:56

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

        try:
            fn = os.path.relpath(fn, start=os.path.dirname(__import__(package).__file__))
        except ValueError:
            return None
    likely benign LLM, confidence 0.90 Imports package by name to compute relative source path for docs.
    Reasoning

    Imports the package by name to compute a relative file path for linking in docs; standard sphinx extension logic with no malicious indicators.

  16. info warn → info by review dynamic-attribute sklearn/__init__.py:134:20

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

            try:
                return globals()[name]
            except KeyError:
                raise AttributeError(f"Module 'sklearn' has no attribute '{name}'")
    likely benign LLM, confidence 0.90 Module-level __getattr__ performing lazy attribute lookup in globals.
    Reasoning

    Module __getattr__ lazily resolving submodule names via globals() is a standard lazy-loading idiom; it raises AttributeError on miss and does nothing hidden.

  17. info warn → info by review dynamic-attribute sklearn/datasets/__init__.py:166:16

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

        try:
            return globals()[name]
        except KeyError:
            # This is turned into the appropriate ImportError
    likely benign LLM, confidence 0.90 Lazy module attribute lookup via globals in datasets package.
    Reasoning

    Same standard module __getattr__ lazy-loading pattern for datasets submodule attributes; benign.

  18. info warn → info by review frame-introspection sklearn/tests/metadata_routing_common.py:47:13

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

        ]
        stack = inspect.stack()
        # for callee, extract the innermost `function` (which is in METHODS) from the
        # stack; for caller, extract the *outermost* matching function so the expected
    likely benign LLM, confidence 0.94 Test helper that calls inspect.stack() to find the calling method names for metadata routing checks.
    Reasoning

    inspect.stack() in a test helper walks the call stack to match the callee and caller method names for metadata routing assertions. The snippet comments describe that purpose, and nothing in it exfiltrates data, runs fetched code, or hides behavior. This is ordinary test introspection in a well-known project.

  19. info critical → info by review dynamic-import sklearn/tests/test_common.py:147:19

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

                continue
            package = __import__(modname, fromlist="dummy")
            for name in getattr(package, "__all__", ()):
                assert hasattr(package, name), "Module '{0}' has no attribute '{1}'".format(
    likely benign LLM, confidence 0.94 Test loop that dynamically imports sklearn submodules to verify their __all__ exports.
    Reasoning

    __import__ is called on modname while iterating over sklearn submodules and checking each package's __all__ attribute. The module names come from the package's own discovery, not from untrusted input. The critical severity reflects the pattern alone, not any malicious intent.

  20. info critical → info by review dynamic-import sklearn/utils/tests/test_estimator_checks.py:1729:13

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

            try:
                importlib.__import__(lib)
            except ImportError:
                raise SkipTest(f"Library {lib} is not installed")
    likely benign LLM, confidence 0.95 Test helper that tries to import an optional library and skips the test if it is not installed.
    Reasoning

    importlib.__import__(lib) is wrapped in try/except ImportError and raises SkipTest when an optional library is missing. This is a standard way to skip tests for optional dependencies. The lib name comes from the test's own hardcoded set of libraries, not from external input.

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/scikit-learn/scikit-learn scikit-learn && cd scikit-learn
git checkout 1.9.1
disclude scan --severity info --diff 1.9.0 .