PyPI / setuptools / 84.2.1

setuptools 84.2.1

Source
https://github.com/pypa/setuptools
Tag scanned
v84.2.1
Compared with
v84.2.0 (0 signals introduced since)
Files
415 scanned, 127 skipped (binary, oversized, or unrecognised)
Signals
1 critical · 0 warn · 58 info
Scan time
17.0 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 22 of 22 warn and critical signals; 21 demoted to info.
Disclude version
4.0.0
Scanned (UTC)
2026-10-21 00:56

Signals

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

  1. critical dynamic-execution launcher.c:328:9

    `execv` called with a non-literal path (ast pass, confidence 0.90)

            /* Use exec, we don't need to wait for the GUI to finish */
            execv(ptr, (const char * const *)(newargs));
            return fail("Could not exec %s", ptr);   /* shouldn't get here! */
        }
    likely benign LLM, confidence 0.60 A C launcher calls execv on a runtime-resolved binary path to start the GUI application.
    Reasoning

    execv with a runtime-computed path is a common launcher pattern (exec the real GUI binary after setup). The snippet alone does not show where ptr comes from, so malicious use cannot be excluded, but the surrounding code reads as a legitimate launcher that replaces itself with the target binary.

  2. info critical → info by review dynamic-execution setuptools/_distutils/command/bdist_rpm.py:369:15

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

    
            out = os.popen(q_cmd)
            try:
                binary_rpms = []
    dismissed LLM, confidence 0.90 Runs an rpm query command via os.popen while building RPM packages.
    Reasoning

    os.popen on a constructed rpm query command is standard distutils code that queries rpm for output file names. Legitimate build tooling, not obfuscation.

  3. info warn → info by review dynamic-execution setuptools/_distutils/command/bdist_rpm.py:436:23

    `subprocess.getoutput` called with a literal shell command (ast pass, confidence 0.70)

            # RHEL6 (and probably derivatives)
            vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}')
            # Generate a potential replacement value for __os_install_post (whilst
            # normalizing the whitespace to simplify the test for whether the
    dismissed LLM, confidence 0.95 Fetches the RPM __os_install_post macro value with a literal shell command.
    Reasoning

    A fixed literal rpm --eval command via subprocess.getoutput is ordinary build-time introspection in the RPM builder.

  4. info warn → info by review dynamic-execution setuptools/_distutils/compilers/C/base.py:1130:28

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

            expected = '"static", "shared", "dylib", "xcode_stub"'
            if lib_type not in eval(expected):
                raise ValueError(f"'lib_type' must be {expected}")
            fmt = getattr(self, lib_type + "_lib_format")
    likely benign LLM, confidence 0.90 Evaluates a hardcoded string literal of library type names to validate lib_type.
    Reasoning

    eval on a fixed local string of quoted format names is a known idiom in distutils; the input is not attacker-controlled in this snippet.

  5. info warn → info by review dynamic-execution setuptools/_distutils/core.py:250:17

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

                    code = f.read().replace(r'\r\n', r'\n')
                    exec(code, g)  # noqa: S102 # executing the setup script is the point
            finally:
                sys.argv = save_argv
    likely benign LLM, confidence 0.90 Executes the project's setup.py script inside run_setup.
    Reasoning

    Executing the setup script via exec is the documented purpose of distutils run_setup; the comment acknowledges it. Expected behavior for a build tool.

  6. info critical → info by review dynamic-import setuptools/_distutils/dist.py:873:17

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

                try:
                    __import__(module_name)
                    module = sys.modules[module_name]
                except ImportError:
    likely benign LLM, confidence 0.85 Dynamically imports a named distutils command module.
    Reasoning

    Dynamic __import__ of a command or module name during option handling is standard distutils plugin loading, guarded by ImportError handling.

  7. identifier `Dεbug` mixes scripts: Latin + Greek (in string/comment) (raw pass, confidence 0.40)

            caplog.set_level(logging.DEBUG)
            log.debug('Dεbug\tMėssãge')
            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  8. identifier `Fαtal` contains homoglyph candidates: α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

            log.debug('Dεbug\tMėssãge')
            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  9. identifier `Fαtal` mixes scripts: Latin + Greek (in string/comment) (raw pass, confidence 0.40)

            log.debug('Dεbug\tMėssãge')
            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  10. identifier `Dεbug` mixes scripts: Latin + Greek (in string/comment) (raw pass, confidence 0.40)

            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  11. identifier `Fαtal` contains homoglyph candidates: α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  12. identifier `Fαtal` mixes scripts: Latin + Greek (in string/comment) (raw pass, confidence 0.40)

            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  13. info warn → info by review dynamic-execution setuptools/_distutils/util.py:397:21

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

                        log.info("byte-compiling %s to %s", file, cfile_base)
                        compile(file, cfile, dfile)
                    else:
                        log.debug("skipping byte-compilation of %s to %s", file, cfile_base)
    dismissed LLM, confidence 0.90 Byte-compiles a Python source file during installation.
    Reasoning

    compile of a source file to bytecode during install is ordinary byte-compilation logic in distutils util.

  14. info warn → info by review decoder-decompress-payload setuptools/_vendor/backports/tarfile/__init__.py:479:20

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

            """
            self.cmp = self.zlib.decompressobj(-self.zlib.MAX_WBITS)
            self.dbuf = b""
    dismissed LLM, confidence 0.80 Creates a zlib decompressor for streaming reads of compressed tar archives.
    Reasoning

    decompressobj is used for streaming tar decompression of the archive being read, which is the purpose of the tarfile module, not a hidden embedded payload.

  15. info warn → info by review dynamic-execution setuptools/_vendor/jaraco/functools/__init__.py:563:28

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

                    try:
                        return eval(use)
                    except TypeError:
                        return replace
    likely benign LLM, confidence 0.75 Evaluates a caller-supplied expression in a vendored functools helper.
    Reasoning

    eval of a 'use' expression in a retry/replace helper is a vendored utility pattern; the input is developer-supplied, not external. Mild concern only.

  16. info warn → info by review dynamic-execution setuptools/_vendor/packaging/licenses/__init__.py:144:9

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

        try:
            compile(python_expression, "", "eval")
        except SyntaxError:
            message = f"Invalid license expression: {raw_license_expression!r}"
    dismissed LLM, confidence 0.90 Compiles a license expression to check its syntax without running it.
    Reasoning

    compile with mode eval is used purely to validate a license expression syntax and is not executed.

  17. info warn → info by review dynamic-execution setuptools/build_meta.py:317:13

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

            try:
                exec(code, locals())  # noqa: S102 # exec is intentional here
            except SystemExit as e:
                if e.code:
    likely benign LLM, confidence 0.85 Executes setup.py during the PEP 517 build backend run.
    Reasoning

    build_meta executes the project's setup script in a controlled namespace as part of the PEP 517 build hook. Intentional and documented.

  18. info warn → info by review dynamic-execution setuptools/command/bdist_egg.py:396:12

    `marshal.load` deserializes Python bytecode from bytes — code-object sink (ast pass, confidence 0.75)

        f.read(skip)
        code = marshal.load(f)
        f.close()
        safe = True
    likely benign LLM, confidence 0.80 Loads compiled module bytecode to scan it for safe-import checks.
    Reasoning

    marshal.load reads bytecode from a file during egg byte-code analysis (checking for symbol references). Standard for this module, though marshal is a sensitive deserializer.

  19. info warn → info by review frame-introspection setuptools/command/install.py:114:18

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

    
            frames = inspect.getouterframes(run_frame)
            for frame in frames[2:4]:
                (caller,) = frame[:1]
    dismissed LLM, confidence 0.90 Inspects the call stack to find the frame that invoked the install command.
    Reasoning

    getouterframes is used to locate the caller of run_setup for install-time behavior. Benign introspection.

  20. line length 1035 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system must be object", value=data__buildsystem, name="" + (name_prefix or "data") + ".build-system", definition={'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': 
  21. line length 792 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups must be object", value=data__dependencygroups, name="" + (name_prefix or "data") + ".dependency-groups", definition={'type': 'object', 'description': 'Dependency groups following PEP 735', 'additionalProperties': False, 'p
  22. line length 891 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be object", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.
  23. line length 968 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be object", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using
  24. line length 934 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be object", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded',
  25. line length 1720 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".ext-modules must be array", value=data__extmodules, name="" + (name_prefix or "data") + ".ext-modules", definition={'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$id': '#/definitions/ext-module
  26. line length 791 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".data-files must be object", value=data__datafiles, name="" + (name_prefix or "data") + ".data-files", definition={'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns t
  27. line length 1584 characters (raw pass, confidence 0.50)

            raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'req
  28. line length 1643 characters (raw pass, confidence 0.50)

                raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class
  29. line length 1636 characters (raw pass, confidence 0.50)

                raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extens
  30. line length 1230 characters (raw pass, confidence 0.50)

            raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$desc
  31. line length 1110 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".find must be object", value=data__find, name="" + (name_prefix or "data") + ".find", definition={'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.']
  32. line length 1282 characters (raw pass, confidence 0.50)

                raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties':
  33. line length 1502 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count12) + " matches found)"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'$$description': ['`Full/detailed description of
  34. line length 935 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count14) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license <https://pe
  35. line length 963 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".authors must be array", value=data__authors, name="" + (name_prefix or "data") + ".authors", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-062
  36. line length 920 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".maintainers must be array", value=data__maintainers, name="" + (name_prefix or "data") + ".maintainers", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python
  37. line length 837 characters (raw pass, confidence 0.50)

                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + " must be one of ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scrip
  38. line length 968 characters (raw pass, confidence 0.50)

            raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capa
  39. line length 1020 characters (raw pass, confidence 0.50)

                raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped to
  40. line length 1011 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + " must be named by propertyName definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped toge
  41. info warn → info by review dynamic-execution setuptools/depends.py:132:24

    `marshal.load` deserializes Python bytecode from bytes — code-object sink (ast pass, confidence 0.75)

                    f.read(8)  # skip magic & date
                    code = marshal.load(f)
                elif kind == PY_FROZEN:
                    code = _imp.get_frozen_object(module, paths)
    likely benign LLM, confidence 0.80 Reads a module's bytecode to extract symbol constants for dependency checks.
    Reasoning

    marshal.load of a module's compiled code is the standard way depends.py extracts constants from installed modules; reading local bytecode files, not fetched payloads.

  42. info warn → info by review decoder-import-with-exec setuptools/depends.py:136:24

    file imports decoder module(s) marshal and calls `exec`/`eval`/`compile` — multi-stage payload shape (ast pass, confidence 0.80)

                elif kind == PY_SOURCE:
                    code = compile(f.read(), path, 'exec')
                else:
                    # Not something we can parse; we'll have to import it.  :(
    likely benign LLM, confidence 0.90 Compiles source read from a module file in setuptools' dependency-inspection code.
    Reasoning

    The compile call reads a module's source file from disk to inspect or load it, which is standard behavior for a module-introspection utility. The marshal import and compile together reflect ordinary bytecode and source handling, not a hidden payload stage.

  43. info warn → info by review dynamic-execution setuptools/depends.py:136:24

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

                elif kind == PY_SOURCE:
                    code = compile(f.read(), path, 'exec')
                else:
                    # Not something we can parse; we'll have to import it.  :(
    likely benign LLM, confidence 0.90 Same compile of a module file's source, flagged separately as non-literal compile.
    Reasoning

    This is the same compile call as the previous finding, flagged again by the dynamic-execution signal. It compiles source from a module file that the library itself located, so the non-literal argument is expected.

  44. info critical → info by review dynamic-import setuptools/extension.py:24:9

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

            # from (cython_impl) import build_ext
            __import__(cython_impl, fromlist=['build_ext']).build_ext  # noqa: B018 # evaluated to trigger validation/side effect
        except Exception:  # noqa: BLE001 # intentional broad fallback
            return False
    likely benign LLM, confidence 0.85 Dynamic import used as an optional Cython availability check inside a try block.
    Reasoning

    The import of cython_impl is wrapped in a broad try/except that returns False on failure. It checks whether Cython is available, which is a common feature-detection pattern. The critical severity comes from the dynamic-import signal, not from anything malicious in the snippet.

  45. info warn → info by review dynamic-execution setuptools/launch.py:31:12

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

        norm_script = script.replace('\\r\\n', '\\n')
        code = compile(norm_script, script_name, 'exec')
        exec(code, namespace)  # noqa: S102 # exec is intentional here
    likely benign LLM, confidence 0.90 Compiles a setup script's text in the launcher, as intended.
    Reasoning

    This is setuptools' script launcher, which runs a setup script inside a namespace by design. Compiling the script's text is the documented purpose of the module, and the comment marks the exec as intentional.

  46. info warn → info by review dynamic-execution setuptools/launch.py:32:5

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

        code = compile(norm_script, script_name, 'exec')
        exec(code, namespace)  # noqa: S102 # exec is intentional here
    likely benign LLM, confidence 0.90 Executes the setup script in the launcher's namespace, by design.
    Reasoning

    This exec runs the compiled setup script in the launcher's namespace. That is the module's stated function, and the code is not obfuscated or fetched from elsewhere.

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

      {name = "John X. Ãørçeč", email = "john@utf8.org"},
      {name = "Γαμα קּ 東", email = "gama@utf8.org"},
    ]
    classifiers = [
  48. identifier `Γαμα` mixes scripts: Greek + Latin (in string/comment) (raw pass, confidence 0.40)

      {name = "John X. Ãørçeč", email = "john@utf8.org"},
      {name = "Γαμα קּ 東", email = "gama@utf8.org"},
    ]
    classifiers = [
  49. identifier `Γαμα` contains homoglyph candidates: α (03B1)→a, α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

                    'Brett Cannon <brett@python.org>, "John X. Ãørçeč" <john@utf8.org>, '
                    'Γαμα קּ 東 <gama@utf8.org>'
                ),
                id='non-international-emails',
  50. identifier `Γαμα` mixes scripts: Greek + Latin (in string/comment) (raw pass, confidence 0.40)

                    'Brett Cannon <brett@python.org>, "John X. Ãørçeč" <john@utf8.org>, '
                    'Γαμα קּ 東 <gama@utf8.org>'
                ),
                id='non-international-emails',
  51. identifier `Γαμα` contains homoglyph candidates: α (03B1)→a, α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

                version = 42
                author_email = "John X. Ãørçeč" <john@utf8.org>, Γαμα קּ 東 <gama@utf8.org>
                long_description = file: README.rst
                """
  52. identifier `Γαμα` mixes scripts: Greek + Latin (in string/comment) (raw pass, confidence 0.40)

                version = 42
                author_email = "John X. Ãørçeč" <john@utf8.org>, Γαμα קּ 東 <gama@utf8.org>
                long_description = file: README.rst
                """
  53. identifier `Γαμα` contains homoglyph candidates: α (03B1)→a, α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

    Version: 42
    Author-email: "John X. Ãørçeč" <john@utf8.org>, Γαμα קּ 東 <gama@utf8.org>
  54. identifier `Γαμα` mixes scripts: Greek + Latin (in string/comment) (raw pass, confidence 0.40)

    Version: 42
    Author-email: "John X. Ãørçeč" <john@utf8.org>, Γαμα קּ 東 <gama@utf8.org>
  55. identifier `Γαμα` contains homoglyph candidates: α (03B1)→a, α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

        assert 'Author-email: "John X. Ãørçeč"' in metadata
        assert "Γαμα קּ 東 " in metadata
        assert "UTF-8 描述 説明" in metadata
  56. identifier `Γαμα` mixes scripts: Greek + Latin (in string/comment) (raw pass, confidence 0.40)

        assert 'Author-email: "John X. Ãørçeč"' in metadata
        assert "Γαμα קּ 東 " in metadata
        assert "UTF-8 描述 説明" in metadata
  57. info critical → info by review dynamic-import setuptools/tests/test_bdist_wheel.py:990:16

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

    
            return importlib.__import__(name, *args, **kwargs)
    
        with suppress(KeyError):
    likely benign LLM, confidence 0.90 Test wrapper around importlib.__import__ used to intercept imports.
    Reasoning

    This is a test helper that wraps importlib.__import__ to observe or patch imports. Test code of this kind is routine and has no network or obfuscation indicators.

  58. info warn → info by review dynamic-execution setuptools/tests/test_editable_install.py:448:9

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

            loc = {}
            exec(finder, loc, loc)  # noqa: S102 # exec is intentional here
            loc["install"]()
    likely benign LLM, confidence 0.90 Test executes the generated editable-finder code in a local namespace.
    Reasoning

    This test executes a generated editable-install finder in an isolated namespace and then calls install. The exec is the thing under test, and the comment marks it as intentional.

  59. info warn → info by review dynamic-execution setuptools/wheel.py:250:64

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

                        raw_req,
                        (req for req in reqs if for_extra(req) and eval(req, extra=extra)),
                    )
                )
    likely benign LLM, confidence 0.70 Evaluates requirement markers to filter requirements for an extra in wheel metadata.
    Reasoning

    The builtin eval does not accept an extra keyword, so this call is probably a local helper or marker evaluator named eval that the snippet does not show. It filters requirements by extra markers, which is ordinary metadata handling. The snippet is short, so some doubt remains.

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/pypa/setuptools setuptools && cd setuptools
git checkout v84.2.1
disclude scan --severity info --diff v84.2.0 .