PyPI / aiohttp / 3.14.5

aiohttp 3.14.5

Source
https://github.com/aio-libs/aiohttp
Tag scanned
v3.14.5
Compared with
v3.14.4 (0 signals introduced since)
Files
240 scanned, 57 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 33 info
Scan time
14.8 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 18 of 18 warn and critical signals; 18 demoted to info.
Disclude version
4.0.0
Scanned (UTC)
2026-10-14 18:17

Signals

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

  1. info long-line CHANGES.rst:6908:1

    line length 1260 characters (raw pass, confidence 0.50)

    - `#3927 <https://github.com/aio-libs/aiohttp/issues/3927>`_, `#4247 <https://github.com/aio-libs/aiohttp/issues/4247>`_, `#4247 <https://github.com/aio-libs/aiohttp/issues/4247>`_, `#5389 <https://github.com/aio-libs/aiohttp/issues/5389>`_, `#5457 <https://github.com/aio-libs/aiohttp/issues/5457>`_, `#5486 <https://gi
  2. info warn → info by review unicode-zero-width CHANGES.rst:8193:3

    U+FEFF ZERO WIDTH NO-BREAK SPACE (BOM) in source (raw pass, confidence 0.75)

    - Rename `from_env` to `trust_env` in client reference. (`#2451 <https://github.com/aio-libs/aiohttp/pull/2451>`_)
    - ⟨U+FEFF⟩Fixed mistype in `Proxy Support` section where `trust_env` parameter was
      used in `session.get("http://python.org", trust_env=True)` method instead of
      aiohttp.ClientSession constructor as follows:
    likely benign LLM, confidence 0.90 A zero-width no-break space (BOM) appears in a CHANGES.rst changelog bullet.
    Reasoning

    A stray U+FEFF byte-order mark sits inside a changelog entry in a documentation file. It is a formatting artifact from copy-paste or editing, with no execution path and no sign of concealment.

  3. info long-line THREAT_MODEL.md:271:1

    line length 961 characters (raw pass, confidence 0.50)

    | 1.12 | Cython ⇄ pure-Python divergence | `tests/test_http_parser.py` parameterises tests over `REQUEST_PARSERS` / `RESPONSE_PARSERS` (pure-Python always; Cython when the extension imports). The high-leverage attack vectors are already covered under both backends: CL+TE (`test_content_length_transfer_encoding`), CL… (340 bytes total)
  4. info warn → info by review frame-introspection aiohttp/client.py:440:62

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

            if loop.get_debug():
                self._source_traceback = traceback.extract_stack(sys._getframe(1))
    
            if cookie_jar is None:
    likely benign LLM, confidence 0.90 Debug-only capture of the caller's stack frame for source tracebacks in ClientSession.
    Reasoning

    sys._getframe(1) is used only inside a loop.get_debug() branch to record a source traceback for unclosed-session diagnostics. This is a standard asyncio debugging idiom.

  5. info warn → info by review frame-introspection aiohttp/client_reqrep.py:524:62

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

            if loop.get_debug():
                self._source_traceback = traceback.extract_stack(sys._getframe(1))
    
        def __reset_writer(self, _: object = None) -> None:
    likely benign LLM, confidence 0.90 Debug-only stack capture for source tracebacks in the client request/response class.
    Reasoning

    Same debug-gated sys._getframe(1) traceback capture used for resource-leak diagnostics. It is not dynamic execution or data exfiltration.

  6. info warn → info by review frame-introspection aiohttp/client_reqrep.py:1103:62

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

            if loop.get_debug():
                self._source_traceback = traceback.extract_stack(sys._getframe(1))
    
            self.update_version(version)
    likely benign LLM, confidence 0.90 Another debug-only sys._getframe traceback capture in client_reqrep.py.
    Reasoning

    Identical debug-gated frame capture pattern in a different method. It is a legitimate asyncio debugging feature.

  7. info warn → info by review decoder-decompress-payload aiohttp/compression_utils.py:143:16

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

        def decompressobj(self, *args: Any, **kwargs: Any) -> ZLibDecompressObjProtocol:
            return self._zlib_backend.decompressobj(*args, **kwargs)
    
        def compress(self, data: Buffer, *args: Any, **kwargs: Any) -> bytes:
    likely benign LLM, confidence 0.90 Wrapper method delegating compression and decompression calls to a zlib backend.
    Reasoning

    This is a backend wrapper that forwards decompressobj and compress calls to zlib or an optional backend. Decompression of HTTP response bodies is the library's core function, not a hidden payload.

  8. info warn → info by review frame-introspection aiohttp/connector.py:148:62

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

            if loop.get_debug():
                self._source_traceback = traceback.extract_stack(sys._getframe(1))
    
        def __repr__(self) -> str:
    likely benign LLM, confidence 0.90 Debug-only stack capture when a connector is created.
    Reasoning

    Debug-gated sys._getframe(1) capture for source traceback of unclosed connectors. Standard diagnostic code.

  9. info warn → info by review frame-introspection aiohttp/connector.py:309:62

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

            if loop.get_debug():
                self._source_traceback = traceback.extract_stack(sys._getframe(1))
    
            # Connection pool of reusable connections.
    likely benign LLM, confidence 0.90 Debug-only stack capture in another connector constructor.
    Reasoning

    Same debug-only frame capture for source traceback in a connector class. Legitimate debugging aid.

  10. info warn → info by review frame-introspection aiohttp/helpers.py:886:17

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

            # Prefix with module name to help deduplicate key names.
            frame = inspect.currentframe()
            while frame:
                if frame.f_code.co_name == "<module>":
    likely benign LLM, confidence 0.90 Walks call frames to find the module name used as a key prefix.
    Reasoning

    inspect.currentframe walks up the stack to the module-level frame to derive a prefix for deduplicating cache or key names. The frame walk is read-only and is used for naming, not for execution of fetched code.

  11. info warn → info by review decoder-decompress-payload aiohttp/multipart.py:614:25

    file imports decoder module(s) base64, binascii and calls `.decompress()` — embedded payload decompressed at runtime (ast pass, confidence 0.75)

                )
                yield await d.decompress(data, max_length=self._max_decompress_size)
                while d.data_available:
                    yield await d.decompress(b"", max_length=self._max_decompress_size)
    likely benign LLM, confidence 0.90 Incremental decompression of multipart content-encoded payload chunks with size limits.
    Reasoning

    Streaming decompression of a request body according to Content-Encoding, with max_length limits applied. It is an expected HTTP feature and the decoded data is only yielded to the application.

  12. info unicode-homoglyph tests/test_client_response.py:469:27

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

            fut = loop.create_future()
            fut.set_result('{"тестkey": "пройденvalue"}'.encode("cp1251"))
            return fut
  13. info unicode-mixed-script tests/test_client_response.py:469:27

    identifier `тестkey` mixes scripts: Cyrillic + Latin (in string/comment) (raw pass, confidence 0.40)

            fut = loop.create_future()
            fut.set_result('{"тестkey": "пройденvalue"}'.encode("cp1251"))
            return fut
  14. info unicode-homoglyph tests/test_client_response.py:469:42

    identifier `пройденvalue` contains homoglyph candidates: р (0440)→p, о (043E)→o, е (0435)→e (in string/comment) (raw pass, confidence 0.35)

            fut = loop.create_future()
            fut.set_result('{"тестkey": "пройденvalue"}'.encode("cp1251"))
            return fut
  15. info unicode-mixed-script tests/test_client_response.py:469:42

    identifier `пройденvalue` mixes scripts: Cyrillic + Latin (in string/comment) (raw pass, confidence 0.40)

            fut = loop.create_future()
            fut.set_result('{"тестkey": "пройденvalue"}'.encode("cp1251"))
            return fut
  16. info warn → info by review decoder-decompress-payload tests/test_compression_utils.py:44:16

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

            self._feeds.append(len(data))
            return self._obj.decompress(data, max_length)
    likely benign LLM, confidence 0.90 Test fake that wraps a decompressor and records the size of each feed.
    Reasoning

    A test helper wrapping a decompressor object to record feed sizes. It exercises the library's own decompression code and is not a payload dropper.

  17. info warn → info by review payload-bytes-literal tests/test_cookiejar.py:1259:19

    bytes literal contains 945 `\xNN` escapes (64% of content) — binary payload shape (ast pass, confidence 0.85)

            pickled = b"\x80\x04\x95\xc8\x0b\x00\x00\x00\x00\x00\x00\x8c\x0bcollections\x94\x8c\x0bdefaultdict\x94\x93\x94\x8c\x0chttp.cookies\x94\x8c\x0cSimpleCookie\x94\x93\x94\x85\x94R\x94(\x8c\x00\x94h\x08\x86\x94h\x05)\x81\x94\x8c\rshared-cookie\x94h\x03\x8c\x06Morsel\x94\x93\x94)\x81\x94(\x8c\x07expires\x94h\x08\x8c\
    likely benign LLM, confidence 0.85 Pickled cookie-jar binary fixture embedded as a bytes literal in a test.
    Reasoning

    The bytes literal is a pickle of a defaultdict/SimpleCookie/Morsel fixture, used to test loading a legacy pickled cookie jar. Hex-escaped binary is expected for pickle fixtures. The pickle is only loaded in a test, but pickle deserialization is a known risk, so confidence is not maximal.

  18. info warn → info by review payload-bytes-literal tests/test_cookiejar.py:1261:19

    bytes literal contains 685 `\xNN` escapes (66% of content) — binary payload shape (ast pass, confidence 0.85)

            pickled = b'\x80\x05\x95\x06\x08\x00\x00\x00\x00\x00\x00\x8c\x0bcollections\x94\x8c\x0bdefaultdict\x94\x93\x94\x8c\x0chttp.cookies\x94\x8c\x0cSimpleCookie\x94\x93\x94\x85\x94R\x94(\x8c\x00\x94h\x08\x86\x94h\x05)\x81\x94\x8c\rshared-cookie\x94h\x03\x8c\x06Morsel\x94\x93\x94)\x81\x94(\x8c\x07expires\x94h\x08\x8c\
    likely benign LLM, confidence 0.90 Test fixture containing a pickled cookie jar as a bytes literal.
    Reasoning

    A bytes literal of pickle protocol 5 data inside a cookie jar test is a test fixture for unpickling cookie data; the payload is a serialized SimpleCookie/defaultdict, consistent with testing pickle round-trip of the cookie jar. Test fixture context makes this benign, though binary test data is worth noting.

  19. info unicode-mixed-script tests/test_http_parser.py:954:65

    identifier `chunKed` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

        # K = Kelvin sign, not valid ascii.
        text = "GET /test HTTP/1.1\r\nHost: a\r\nTransfer-Encoding: chunKed\r\n\r\n"
        with pytest.raises(http_exceptions.BadHttpMessage):
            parser.feed_data(text.encode())
  20. info unicode-mixed-script tests/test_http_parser.py:961:55

    identifier `websocKet` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

        # K = Kelvin sign, not valid ascii.
        text = "GET /test HTTP/1.1\r\nHost: a\r\nUpgrade: websocKet\r\n\r\n"
        messages, upgrade, tail = parser.feed_data(text.encode())
        assert not upgrade
  21. info unicode-mixed-script tests/test_http_parser.py:1125:10

    identifier `zstd` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

        (
            "zstd".encode(),  # "st".upper() == "ST"
            "deflate".encode(),  # "fl".upper() == "FL"
        ),
  22. info unicode-mixed-script tests/test_http_parser.py:1126:10

    identifier `deflate` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

            "zstd".encode(),  # "st".upper() == "ST"
            "deflate".encode(),  # "fl".upper() == "FL"
        ),
    )
  23. info warn → info by review decoder-decompress-payload tests/test_http_writer.py:66:9

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

    def decompress(data: bytes) -> bytes:
        d = ZLibBackend.decompressobj()
        return d.decompress(data)
    likely benign LLM, confidence 0.90 Test helper that decompresses zlib data for verifying HTTP writer output.
    Reasoning

    A small helper wrapping zlib decompressobj in a test file for HTTP writer compression testing. Decompressing test response data is ordinary; no hidden payload or execution path.

  24. info warn → info by review decoder-decompress-payload tests/test_multipart.py:1466:20

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

    
        decompressor = ZLibBackend.decompressobj(wbits=16 + ZLibBackend.MAX_WBITS)
        data = decompressor.decompress(message.split(b"\r\n")[0])
        data += decompressor.flush()
    likely benign LLM, confidence 0.90 Test code decompressing a gzip-framed message chunk.
    Reasoning

    Test code using a zlib decompressobj with wbits for gzip-framed multipart data. Standard decompression in a test; the flagged payload is test input, not an executed dropper.

  25. info unicode-homoglyph tests/test_urldispatch.py:473:20

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

        resource = router.add_static(
            "/пре %2Fфикс", pathlib.Path(aiohttp.__file__).parent, name="static"
        )
        assert router["static"] is resource
  26. info unicode-mixed-script tests/test_urldispatch.py:473:20

    identifier `Fфикс` mixes scripts: Latin + Cyrillic (in string/comment) (raw pass, confidence 0.40)

        resource = router.add_static(
            "/пре %2Fфикс", pathlib.Path(aiohttp.__file__).parent, name="static"
        )
        assert router["static"] is resource
  27. info unicode-homoglyph tests/test_urldispatch.py:683:49

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

        handler = make_handler()
        route = router.add_route("GET", r"/пре %2Fфикс/{arg}", handler)
    
        url = route.url_for(arg="1 2/текст%2F")
  28. info unicode-mixed-script tests/test_urldispatch.py:683:49

    identifier `Fфикс` mixes scripts: Latin + Cyrillic (in string/comment) (raw pass, confidence 0.40)

        handler = make_handler()
        route = router.add_route("GET", r"/пре %2Fфикс/{arg}", handler)
    
        url = route.url_for(arg="1 2/текст%2F")
  29. info unicode-homoglyph tests/test_urldispatch.py:771:21

    identifier `рус` contains homoglyph candidates: р (0440)→p, у (0443)→y, с (0441)→c (in string/comment) (raw pass, confidence 0.35)

        match_info = await router.resolve(req)
        assert {"var": "рус текст"} == match_info
  30. info warn → info by review dynamic-attribute tests/test_web_functional.py:3042:41

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

        assert resp.status == 200
        assert await resp.text() == str(len(locals()[len_of]))
        await resp.release()
    likely benign LLM, confidence 0.92 Assertion reading a local variable via locals() in a web functional test.
    Reasoning

    locals() lookup by a variable name in an assertion is a trivial test idiom for reading a local length value. It does not load or execute external code.

  31. info warn → info by review decoder-decompress-payload tests/test_web_response.py:657:12

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

        await resp.prepare(req)
        assert gzip.decompress(resp._compressed_body) == body
    likely benign LLM, confidence 0.92 Test assertion verifying gzip-compressed response body round-trips correctly.
    Reasoning

    Asserts gzip.decompress of a compressed response body equals the original body. Ordinary test verification of response compression.

  32. info warn → info by review dynamic-execution tools/gen.py:16:12

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

        hdrs_file = root / "aiohttp/hdrs.py"
        code = compile(hdrs_file.read_text(), str(hdrs_file), "exec")
        globs = {}
        exec(code, globs)
    likely benign LLM, confidence 0.85 Code generator compiling and executing the project's own hdrs.py to collect header names.
    Reasoning

    Build tool compiles and execs aiohttp/hdrs.py, a local project source file, to extract header constants. This is a known pattern in a repo generator script; the input is a fixed in-repo file, not untrusted data. Some residual doubt since exec of file contents is generally risky.

  33. info warn → info by review dynamic-execution tools/gen.py:18:5

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

        globs = {}
        exec(code, globs)
        headers = [val for val in globs.values() if isinstance(val, multidict.istr)]
        return sorted(headers)
    likely benign LLM, confidence 0.85 Code generator executing hdrs.py into a globals dict to extract header values.
    Reasoning

    Same generator pattern: exec of the local hdrs.py source into a namespace to read istr header values. Benign given the fixed local input file and build-tool context.

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/aio-libs/aiohttp aiohttp && cd aiohttp
git checkout v3.14.5
disclude scan --severity info --diff v3.14.4 .