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
713 scanned, 96 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 23 info
Scan time
11.5 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 15 of 15 warn and critical signals; 15 demoted to info.
Disclude version
4.1.0
Scanned (UTC)
2026-10-21 20:52

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.90 Installs Rust via rustup's official curl-pipe-sh installer.
    Reasoning

    Piping curl output from the official rustup domain into sh is the documented rustup install method; it is a known supply-chain-risky pattern but is the vendor's standard install, with TLS 1.2 and https enforced.

  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

    Sourcing $HOME/.cargo/env is the standard post-rustup step to update PATH; the path is a fixed user-home file written by the installer.

  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.90 Installs uv via astral's official curl-pipe-sh script.
    Reasoning

    Piping the official astral.sh uv installer into sh is the documented install method for uv, a legitimate build-setup step in a docs build script.

  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.85 Documentation snippet recommending the official uv curl-pipe-sh installer.
    Reasoning

    Contributor documentation showing the official uv install one-liner followed by pre-commit installation; a standard install instruction, not hidden execution.

  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
    likely benign LLM, confidence 0.90 Test fixture of base64 encodings for JSON bytes serialization.
    Reasoning

    The base64-like string is a test fixture comparing standard and URL-safe base64 encodings of bytes; expected test data, 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.85 Lazy attribute loader caching imported submodules into module globals.
    Reasoning

    Lazy-loading module attributes via globals() caching after import_module is the standard PEP 562-style __getattr__ pattern in pydantic's public API.

  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.90 Walks the call stack to extract class source for docstrings.
    Reasoning

    inspect.currentframe is used to locate source for docstring extraction; ordinary 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.90 Retrieves caller frame to resolve generic model namespace.
    Reasoning

    sys._getframe to find the caller's namespace for generic parametrization is a known, legitimate pydantic technique with a clear error path.

  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.90 Reads parent frame to detect generic parameter scope in typing helpers.
    Reasoning

    Frame access to detect generic-parameter scopes is ordinary typing-introspection code in pydantic internals.

  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.90 Reads caller's globals to set model __module__ name.
    Reasoning

    Caller-frame lookup to set __module__ when not provided is a standard metaclass idiom in pydantic.

  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.90 Fetches parent frame to resolve type namespace in TypeAdapter.
    Reasoning

    Parent frame retrieval for namespace resolution in TypeAdapter is expected library behavior.

  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.90 Reads caller frame globals to build namespace resolver.
    Reasoning

    Frame globals access for parent namespace depth is legitimate namespace resolution, documented in nearby comments.

  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.90 Retrieves caller frame for generic model namespace in v1 code.
    Reasoning

    Same legitimate caller-frame lookup used for generic model creation in pydantic v1 compatibility code.

  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 executes a local source string to check field descriptions.
    Reasoning

    exec on a test-defined source string to verify docstring extraction behavior; test code operating on literal fixtures.

  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 execs a code string with injected globals to exercise plugin behavior.
    Reasoning

    exec of a small test code string with a restricted globals dict inside a plugin test; expected test behavior, not an attack.

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