PyPI / pydantic / 2.14.1

pydantic 2.14.1

Source
https://github.com/pydantic/pydantic
Tag scanned
v2.14.1
Compared with
v2.14.0 (0 signals introduced since)
Files
708 scanned, 101 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 23 info
Scan time
13.3 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 15 of 15 warn and critical signals; 15 demoted to info.
Disclude version
4.0.0
Scanned (UTC)
2026-10-14 18:11

Signals

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

  1. info warn → info by review dynamic-execution build-docs.sh:10:1

    pipeline feeds into `sh` — piped data is executed as shell code (ast pass, confidence 0.80)

    # Install Rust
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
    source $HOME/.cargo/env
    likely benign LLM, confidence 0.80 Installs Rust via the official rustup curl-to-shell installer in a docs build script.
    Reasoning

    The script pipes the official rustup installer from sh.rustup.rs into sh, a known but widely used bootstrap pattern. The URL is the vendor's own, the command enforces TLS 1.2+, and it sits in a docs build script. The risk is the unverified curl-to-shell pattern, not evidence of malicious intent.

  2. info warn → info by review dynamic-import build-docs.sh:11:1

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

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
    source $HOME/.cargo/env
    
    # Install uv
    likely benign LLM, confidence 0.85 Sources the cargo environment file after installing Rust.
    Reasoning

    The sourced path is $HOME/.cargo/env, the standard file the rustup installer creates. The variable is the user's home directory, so this is an ordinary environment setup step.

  3. info warn → info by review dynamic-execution build-docs.sh:14:1

    pipeline feeds into `sh` — piped data is executed as shell code (ast pass, confidence 0.80)

    # Install uv
    curl -LsSf https://astral.sh/uv/install.sh | sh
    
    uv sync --python 3.12 --group docs --frozen
    likely benign LLM, confidence 0.80 Installs uv via the official astral.sh curl-to-shell installer.
    Reasoning

    This is the official astral.sh uv installer piped to sh, a standard install method. It is a supply-chain trust decision rather than obfuscation, and the surrounding script is a normal build setup.

  4. info warn → info by review dynamic-execution docs/contributing.md:79:1

    [embedded bash] pipeline feeds into `sh` — piped data is executed as shell code (ast pass, confidence 0.80)

    # https://pre-commit.com/#install
    curl -LsSf https://astral.sh/uv/install.sh | sh
    uv tool install pre-commit
    likely benign LLM, confidence 0.80 Contributor docs show installing uv via the official curl-to-shell script.
    Reasoning

    This is an embedded install instruction in contributor documentation, pointing to the same official uv installer and linked to pre-commit's docs. It is documentation, not executed code in the project.

  5. info warn → info by review encoding-base64 pydantic-core/tests/test_json.py:193:22

    base64-like blob (44 bytes, compression ratio 1.25) (raw pass, confidence 0.60)

        data = b'\xd8\x07\xc1Tx$\x91F%\xf3\xf3I\xca\xd8@\x0c\xee\xc3\xab\xff\x7f\xd3\xcd\xcd\xf9\xc2\x10\xe4\xa1\xb01e'
        encoded_std = b'"2AfBVHgkkUYl8/NJythADO7Dq/9/083N+cIQ5KGwMWU="'
        encoded_url = b'"2AfBVHgkkUYl8_NJythADO7Dq_9_083N-cIQ5KGwMWU="'
        assert to_json(data, bytes_mode='base64') == encoded_url
    dismissed LLM, confidence 0.97 Test fixture containing base64-encoded bytes used to verify JSON serialization.
    Reasoning

    The blob is a base64 encoding of 32 bytes of random-looking test data, shown alongside its standard and URL-safe encoded forms and used in an assertion comparing to_json output. This is a unit test fixture for bytes_mode='base64', not a payload.

  6. identifier `а` contains homoglyph candidates: а (0430)→a (in string/comment) (raw pass, confidence 0.35)

            ),
            pytest.param('а' * 25, 32, None, id='a lot of `а`s'),
        ],
    )
  7. identifier `а` contains homoglyph candidates: а (0430)→a (in string/comment) (raw pass, confidence 0.35)

            ),
            pytest.param('а' * 25, 32, None, id='a lot of `а`s'),
        ],
    )
  8. identifier `pasℝs` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

            (
                'http://usßer:pasℝs@a💣b.com:123/c?d=e&d=f#g',
                {
                    'str()': 'http://us%C3%9Fer:pas%E2%84%9Ds@xn--ab-qt72a.com:123/c?d=e&d=f#g',
  9. `btoa` encodes a value as base64 at runtime — used in exfiltration patterns (ast pass, confidence 0.55)

          let buffer = await blob.arrayBuffer();
          return btoa(new Uint8Array(buffer).reduce((data, byte) => data + String.fromCharCode(byte), ''));
        }
      } else {
  10. info warn → info by review dynamic-attribute pydantic/__init__.py:448:9

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

            result = import_module(f'.{attr_name}', package=package)
            globals()[attr_name] = result
            return result
        else:
    likely benign LLM, confidence 0.92 Lazy submodule loader that caches imported attributes into module globals.
    Reasoning

    This is the standard lazy-loading pattern in pydantic's module __getattr__: it imports a submodule by name and caches it in globals(). It is a common, legitimate Python idiom for deferred imports.

  11. info warn → info by review frame-introspection pydantic/_internal/_docs_extraction.py:53:13

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

    def _extract_source_from_frame(cls: type[Any]) -> list[str] | None:
        frame = inspect.currentframe()
    
        while frame:
    likely benign LLM, confidence 0.93 Uses inspect.currentframe to walk frames while extracting class docs.
    Reasoning

    Walking the call stack with inspect.currentframe is how pydantic extracts source docstrings for class fields. This is expected introspection in a validation library.

  12. info warn → info by review frame-introspection pydantic/_internal/_generics.py:177:33

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

        try:
            previous_caller_frame = sys._getframe(depth)
        except ValueError as e:
            raise RuntimeError('This function must be used inside another function') from e
    likely benign LLM, confidence 0.93 Fetches a caller frame to resolve the generic parameterization context.
    Reasoning

    sys._getframe(depth) is used to find the caller's module for generic parameter resolution, with a proper ValueError guard. This is standard internal machinery in pydantic's generics code.

  13. info warn → info by review frame-introspection pydantic/_internal/_typing_extra.py:173:13

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

        """
        frame = sys._getframe(parent_depth)
    
        if frame.f_code.co_name.startswith('<generic parameters of'):
    likely benign LLM, confidence 0.93 Reads a parent stack frame to detect generic parameter scopes.
    Reasoning

    Reads a parent frame to detect generic-parameter scopes and namespaces. This is a typical type-resolution technique, with no code execution or exfiltration.

  14. info warn → info by review frame-introspection pydantic/main.py:1911:13

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

        if __module__ is None:
            f = sys._getframe(1)
            __module__ = f.f_globals['__name__']
    likely benign LLM, confidence 0.93 Reads the caller frame's module name when __module__ is unset.
    Reasoning

    When __module__ is not given, the code takes the caller's __name__ from frame globals. This is a common way to set a class's module, and it reads only metadata.

  15. info warn → info by review frame-introspection pydantic/type_adapter.py:217:17

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

        def _fetch_parent_frame(self) -> FrameType | None:
            frame = sys._getframe(self._parent_depth)
            if frame.f_globals.get('__name__') == 'typing':
                # Because `TypeAdapter` is generic, explicitly parametrizing the class results
    likely benign LLM, confidence 0.93 Fetches the parent frame to resolve TypeAdapter namespaces.
    Reasoning

    Fetches the parent frame to resolve the namespace of a TypeAdapter, with a typing-module check. This is expected behavior for namespace resolution in pydantic.

  16. info warn → info by review frame-introspection pydantic/type_adapter.py:355:20

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

            # and so we skip the globalns = get_module_ns_of(typ) call that would normally happen
            globalns = sys._getframe(max(_parent_namespace_depth - 1, 1)).f_globals
            ns_resolver = _namespace_utils.NsResolver(
    likely benign LLM, confidence 0.93 Reads the caller's globals to build a namespace resolver.
    Reasoning

    Takes the caller's f_globals as a namespace, with a bounded depth and a comment explaining the logic. It is internal namespace resolution and nothing more.

  17. info warn → info by review frame-introspection pydantic/v1/generics.py:368:33

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

        try:
            previous_caller_frame = sys._getframe(2)
        except ValueError as e:
            raise RuntimeError('This function must be used inside another function') from e
    likely benign LLM, confidence 0.93 Fetches a caller frame to resolve generic parameters in pydantic v1.
    Reasoning

    This is the same caller-frame lookup used for generics in pydantic v1, with a ValueError guard. It is standard, legitimate library internals.

  18. info warn → info by review dynamic-execution tests/test_docs_extraction.py:432:5

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

    
        exec(source, globals(), locals_dict)
        assert locals_dict['MyModel'].model_fields['a'].description is None
    likely benign LLM, confidence 0.90 Test helper executes generated model source to verify docstring extraction behavior.
    Reasoning

    This is a pydantic test that execs a source string built in the test to define a model and then checks the resulting class's field description. The exec is on test-generated source with explicit namespace dicts, which is a common pattern for testing docstring extraction. No network, shell, or encoded payload is visible.

  19. info unicode-mixed-script tests/test_networks.py:466:17

    identifier `exaMplФ` mixes scripts: Latin + Cyrillic (in string/comment) (raw pass, confidence 0.40)

            ('ftp://example.com:21', 21),
            ('ftp://exaMplФ.com:221/path/to/ftp', 221),
            ('ftp://example.com:144', 144),
            ('ftp://example.com:990/path/to/ftp', 990),
  20. info unicode-homoglyph tests/test_networks.py:925:41

    identifier `с` contains homoglyph candidates: с (0441)→c (in string/comment) (raw pass, confidence 0.35)

            (
                'чебурашкаящик-с-апельсинами.рф@example.com',
                'чебурашкаящик-с-апельсинами.рф',
                'чебурашкаящик-с-апельсинами.рф@example.com',
  21. info unicode-homoglyph tests/test_networks.py:926:41

    identifier `с` contains homoglyph candidates: с (0441)→c (in string/comment) (raw pass, confidence 0.35)

                'чебурашкаящик-с-апельсинами.рф@example.com',
                'чебурашкаящик-с-апельсинами.рф',
                'чебурашкаящик-с-апельсинами.рф@example.com',
            ),
  22. info unicode-homoglyph tests/test_networks.py:927:41

    identifier `с` contains homoglyph candidates: с (0441)→c (in string/comment) (raw pass, confidence 0.35)

                'чебурашкаящик-с-апельсинами.рф',
                'чебурашкаящик-с-апельсинами.рф@example.com',
            ),
  23. info warn → info by review dynamic-execution tests/test_plugins.py:408:9

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

    """
            exec(code, {'bar': 'baz'})
    likely benign LLM, confidence 0.90 Test code executes a small snippet in a controlled namespace to exercise plugin behavior.
    Reasoning

    The exec runs a code string with a restricted globals dict {'bar': 'baz'} inside a test that checks plugin behavior. This is test scaffolding for exercising plugin or validation behavior with controlled inputs, with no evident malicious indicators in the snippet.

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/pydantic/pydantic pydantic && cd pydantic
git checkout v2.14.1
disclude scan --severity info --diff v2.14.0 .