Your application may contain no import brotli and still run a vulnerable Brotli decoder. urllib3 (behind requests), httpx and aiohttp decode Brotli automatically when the package is installed. That is why CVE-2026-111497 is a Python audit item and not only a Node.js one.

The primary source here is Aikido's Python package analysis, published 2026-10-23 and updated 10-27. It is a security vendor's write-up with some product promotion. The Oct 24–25 GeekNews attention was only the discovery path. The briefing called it "HTTP2Shell"; the flaw has several nicknames (Node2Shell, Python2Shell, …) and has nothing to do with the HTTP/2 protocol. Track the CVE number.

What is established and what is not

Item Content
Affected Brotli 1.2.0 and earlier (affected from 1.0.3), brotlicffi 1.2.0.x
Fixed Brotli 1.2.1+, brotlicffi 1.2.1.0+
Paths App decodes a Brotli response from an attacker-controlled server, or decodes a request body sent with Content-Encoding: br
CVSS 4.0 score 9.2 per the CVE record; Node.js rates it high
Repro In OSM's test cited by Aikido, 1.2.0 kept asking for more input on a ~23 MB crafted stream; 1.2.1 rejected it with an error
Not established Memory corruption or code execution in the Python extension was not pursued; no Python PoC published; no in-the-wild exploitation claimed for Python

So you cannot write "remote code execution in Python is proven." What is shown is that the same decoder code ships in the Python packages and that 1.2.0 misbehaves on crafted input. Nor does this show RCE is impossible. Patching under uncertainty is cheap.

Why you are affected without importing it

Per Aikido, urllib3 (and so requests) decodes Brotli when Brotli or brotlicffi is installed; httpx does so with the brotli extra; aiohttp when installed. Tools built on them, such as Scrapy, follow. The clients send Accept-Encoding: br and decode automatically, so features that contact arbitrary servers — webhook follow-ups, URL previews, crawlers, an agent's web search or fetch — are exposure paths.

An audit script

Below is the core of a read-only script I wrote (brotli_audit.py). It compares the two packages' installed versions to the fixed ones and greps a lockfile.

import importlib.metadata as md, re
FIXED = {"brotli": (1, 2, 1), "brotlicffi": (1, 2, 1, 0)}
def parse(v): return tuple(int(x) for x in re.findall(r"\d+", v)[:4])
for name, fixed in FIXED.items():
    try: v = md.version(name)
    except md.PackageNotFoundError:
        print(name, "not installed"); continue
    p = parse(v); p += (0,) * (len(fixed) - len(p))
    print(name, v, "OK" if p >= fixed else "VULNERABLE: upgrade")

I ran it on my local Python 3.11.2 with python3 -I. Neither package is installed there, so this does not reproduce a vulnerable case:

brotli      -          not installed
brotlicffi  -          not installed
urllib3 default Accept-Encoding: gzip,deflate
lockfile sample hits: ['Brotli==1.1.0', 'httpx[brotli]==0.30']

As the third line shows, without Brotli the default Accept-Encoding omits br; installing the package widens what your client advertises, so check real images. Aikido's detection commands are pip show Brotli brotlicffi, docker run --rm <image> pip show Brotli brotlicffi per image, and pipdeptree -r -p Brotli. With many virtualenvs or images, run per image.

Fixes and their limits

  1. pip install -U "Brotli>=1.2.1" or "brotlicffi>=1.2.1.0", update lockfiles, rebuild images and redeploy.
  2. If you cannot upgrade yet, removing Brotli packages and the httpx[brotli] extra is a workaround. Aikido says requests, urllib3 and httpx keep gzip and deflate; a server that sends Brotli anyway may cause errors.
  3. For inbound bodies, reject Content-Encoding: br at the edge. That does not cover responses your app fetches. Dropping br from Accept-Encoding is reportedly insufficient: a malicious server can send Brotli regardless.
  4. Uninstalling does not remove decoders bundled in other runtimes or products. Node.js needs its own patch; the same article lists 22.23.4, 24.22.1 and 26.12.1.

One caution: another write-up I found said that as of Oct 23 advisory databases did not map the CVE to the PyPI packages, so pip-audit, Dependabot or Safety might miss it, while Aikido's updated page (Oct 27) cites PYSEC identifiers. Verify your scanner's current database rather than reading an empty result as safe. I did not cross-check the CVE record (NVD) or the upstream fix commit myself.

Conclusion for developers

Audit transitive dependencies by "who uses it on my behalf," not "do I use it." For a service whose agent fetches arbitrary URLs, decoding an external response happens inside the trust boundary. Prioritize services that contact arbitrary external servers, then services that decompress request bodies. Batch jobs that only call internal APIs can wait, but still check whether Brotli is in the image.