<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet href="/static/style.xsl" type="text/xsl"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
  <channel>
    <title>Most recent entries from osv_homebrew</title>
    <link>https://cve.radiocsirt.org</link>
    <description>Contains only the most 10 recent entries.</description>
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>python-feedgen</generator>
    <language>en</language>
    <lastBuildDate>Fri, 02 Oct 2026 14:16:36 +0000</lastBuildDate>
    <item>
      <title>BREW-snakeviz-GHSA-chx6-46f5-w4vp — tornado: CurlAsyncHTTPClient enforces no response-size limit — decompression bomb drives unbounded memory accumulation…</title>
      <link>https://cve.radiocsirt.org/vuln/brew-snakeviz-ghsa-chx6-46f5-w4vp</link>
      <description>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: snakeviz&lt;/p&gt;
&lt;p&gt;An unbounded memory accumulation (decompression bomb) in `tornado.curl_httpclient.CurlAsyncHTTPClient` — the client-side sibling gap of CVE-2026-49855 — verified end-to-end on the 2026-08-15 master snapshot (`6.6.dev1`) and present unchanged in the latest release tag `v6.5.8` and on master (checked 2026-08-17). When a Tornado application configures the curl client (the documented deployment for proxy support / advanced TLS options) and `fetch()`es an attacker-chosen or attacker-compromised URL with default `decompress_response=True`, a malicious server replying `Content-Encoding: gzip` with a ~2.8 MB wire bomb drove the client&amp;#39;s RSS from 30,884 kB to **1,032,100 kB (~1008 MB) in 3.18 s** (~350 MB/s, monotonic, no plateau) until the kernel OOM-killed the process (exit 137, cgroup `OOMKilled=true`) — with the transfer only 67% complete and **no client-side size check ever intervening**: `curl_httpclient.py` contains zero occurrences of `max_body_size`/`MAXFILESIZE`. The identical bomb against the default `SimpleAsyncHTTPClient` fails cleanly at ~65 MB, because every size gate CVE-2026-49855 added (compressed CL, chunked total, cumulative decompressed size — `http1connection.py:620,676,742`) lives in code the curl client never executes. This is a distinct component from the published advisory (which fixed `_GzipMessageDelegate`/`SimpleAsyncHTTPClient` only) and from the other curl-client advisories (credential handle-reuse GHSA-pw6j-qg29-8w7f, header CRLF GHSA-w235-7p84-xx57);…&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: snakeviz&lt;/p&gt;
&lt;p&gt;An unbounded memory accumulation (decompression bomb) in `tornado.curl_httpclient.CurlAsyncHTTPClient` — the client-side sibling gap of CVE-2026-49855 — verified end-to-end on the 2026-08-15 master snapshot (`6.6.dev1`) and present unchanged in the latest release tag `v6.5.8` and on master (checked 2026-08-17). When a Tornado application configures the curl client (the documented deployment for proxy support / advanced TLS options) and `fetch()`es an attacker-chosen or attacker-compromised URL with default `decompress_response=True`, a malicious server replying `Content-Encoding: gzip` with a ~2.8 MB wire bomb drove the client&amp;#39;s RSS from 30,884 kB to **1,032,100 kB (~1008 MB) in 3.18 s** (~350 MB/s, monotonic, no plateau) until the kernel OOM-killed the process (exit 137, cgroup `OOMKilled=true`) — with the transfer only 67% complete and **no client-side size check ever intervening**: `curl_httpclient.py` contains zero occurrences of `max_body_size`/`MAXFILESIZE`. The identical bomb against the default `SimpleAsyncHTTPClient` fails cleanly at ~65 MB, because every size gate CVE-2026-49855 added (compressed CL, chunked total, cumulative decompressed size — `http1connection.py:620,676,742`) lives in code the curl client never executes. This is a distinct component from the published advisory (which fixed `_GzipMessageDelegate`/`SimpleAsyncHTTPClient` only) and from the other curl-client advisories (credential handle-reuse GHSA-pw6j-qg29-8w7f, header CRLF GHSA-w235-7p84-xx57);…&lt;/p&gt;</content:encoded>
      <guid isPermaLink="false">https://cve.radiocsirt.org/vuln/brew-snakeviz-ghsa-chx6-46f5-w4vp</guid>
      <pubDate>Thu, 01 Oct 2026 11:45:24 +0000</pubDate>
    </item>
    <item>
      <title>BREW-snakeviz-GHSA-c2m8-h5v5-343r — Tornado: StaticFileHandler follows symlinks outside static root (path traversal)</title>
      <link>https://cve.radiocsirt.org/vuln/brew-snakeviz-ghsa-c2m8-h5v5-343r</link>
      <description>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: snakeviz&lt;/p&gt;
&lt;p&gt;### Summary
StaticFileHandler allows an unauthenticated attacker to read arbitrary files from the server&amp;#39;s filesystem by requesting a path that resolves to a symbolic link placed inside the static root directory. Any application that serves user-uploadable content, or whose static directory is populated by a build/deploy pipeline that creates symlinks (e.g. npm link, webpack, Docker volume mounts, CDN sync tools), is affected. An attacker who can trigger the creation of a symlink pointing outside the static root—or exploit one that already exists—can retrieve sensitive files such as /etc/passwd, private keys, configuration files, or application secrets.&lt;/p&gt;
&lt;p&gt;### Details
The vulnerability is in tornado.web.StaticFileHandler, specifically in the interaction between two methods in [tornado/web.py](vscode-webview://1grdaaa6v64itrol06qu70pq3d1b9h9im5c3npvvc9snaprc1q9f/tornado/web.py):
- [get_absolute_path](https://github.com/tornadoweb/tornado/blob/0ca3c5f8279d402b245718d16522bc18a8f5d958/tornado/web.py#L2932) uses `os.path.abspath()` to resolve the requested path:
- [validate_absolute_path](https://github.com/tornadoweb/tornado/blob/0ca3c5f8279d402b245718d16522bc18a8f5d958/tornado/web.py#L2935) uses the same `os.path.abspath()` on the root, then performs a string prefix check:&lt;/p&gt;
&lt;p&gt;`os.path.abspath()` normalises . and .. segments but does not resolve symbolic links. As a result, a path like `/var/www/static/link` passes the `startswith(&amp;#34;/var/www/static/&amp;#34;)` check regardless of where `l…&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: snakeviz&lt;/p&gt;
&lt;p&gt;### Summary
StaticFileHandler allows an unauthenticated attacker to read arbitrary files from the server&amp;#39;s filesystem by requesting a path that resolves to a symbolic link placed inside the static root directory. Any application that serves user-uploadable content, or whose static directory is populated by a build/deploy pipeline that creates symlinks (e.g. npm link, webpack, Docker volume mounts, CDN sync tools), is affected. An attacker who can trigger the creation of a symlink pointing outside the static root—or exploit one that already exists—can retrieve sensitive files such as /etc/passwd, private keys, configuration files, or application secrets.&lt;/p&gt;
&lt;p&gt;### Details
The vulnerability is in tornado.web.StaticFileHandler, specifically in the interaction between two methods in [tornado/web.py](vscode-webview://1grdaaa6v64itrol06qu70pq3d1b9h9im5c3npvvc9snaprc1q9f/tornado/web.py):
- [get_absolute_path](https://github.com/tornadoweb/tornado/blob/0ca3c5f8279d402b245718d16522bc18a8f5d958/tornado/web.py#L2932) uses `os.path.abspath()` to resolve the requested path:
- [validate_absolute_path](https://github.com/tornadoweb/tornado/blob/0ca3c5f8279d402b245718d16522bc18a8f5d958/tornado/web.py#L2935) uses the same `os.path.abspath()` on the root, then performs a string prefix check:&lt;/p&gt;
&lt;p&gt;`os.path.abspath()` normalises . and .. segments but does not resolve symbolic links. As a result, a path like `/var/www/static/link` passes the `startswith(&amp;#34;/var/www/static/&amp;#34;)` check regardless of where `l…&lt;/p&gt;</content:encoded>
      <guid isPermaLink="false">https://cve.radiocsirt.org/vuln/brew-snakeviz-ghsa-c2m8-h5v5-343r</guid>
      <pubDate>Thu, 01 Oct 2026 11:45:24 +0000</pubDate>
    </item>
    <item>
      <title>BREW-snakeviz-GHSA-3hv7-mjh2-fv65 — Tornado: Unbounded query-string argument count allows event-loop-stalling DoS</title>
      <link>https://cve.radiocsirt.org/vuln/brew-snakeviz-ghsa-3hv7-mjh2-fv65</link>
      <description>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: snakeviz&lt;/p&gt;
&lt;p&gt;## Summary&lt;/p&gt;
&lt;p&gt;`HTTPServerRequest.__init__` in `tornado/httputil.py` parses the URL query string via
`parse_qs_bytes()` with no field-count limit — while the sibling POST-body parsing path
(`parse_body_arguments`) received a `max_num_fields=1000` cap added earlier in this exact
same release (v6.5.8, commit `8d6363ed`), explicitly to bound parsing cost for the identical
underlying primitive. This leaves the query-string path with the resource-exhaustion exposure
the body-path fix was meant to close.&lt;/p&gt;
&lt;p&gt;**File**: `tornado/httputil.py`, line 553 (`HTTPServerRequest.__init__`)&lt;/p&gt;
&lt;p&gt;### Root Cause&lt;/p&gt;
&lt;p&gt;```python
# tornado/httputil.py:553 (before fix)
self.arguments = parse_qs_bytes(self.query, keep_blank_values=True)
```&lt;/p&gt;
&lt;p&gt;Compare with the POST-body path fixed one commit earlier in the same release:&lt;/p&gt;
&lt;p&gt;```python
# tornado/httputil.py:1038-1041
uri_arguments = parse_qs_bytes(
    body,
    keep_blank_values=True,
    max_num_fields=config.urlencoded.max_arguments,  # default 1000
)
```&lt;/p&gt;
&lt;p&gt;Both call sites funnel through the same `tornado.escape.parse_qs_bytes` (a thin wrapper over
`urllib.parse.parse_qs`), which is exactly why `max_num_fields` was added to
`urllib.parse.parse_qsl` upstream — to let frameworks bound field count. The fix was applied
only to the body path; the query-string path was missed.&lt;/p&gt;
&lt;p&gt;The request line + headers together are capped at `max_header_size` (default 65536 bytes), so
this is not literally unbounded, but a single ~64KB request line can carry thousands of short
`key=value…&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: snakeviz&lt;/p&gt;
&lt;p&gt;## Summary&lt;/p&gt;
&lt;p&gt;`HTTPServerRequest.__init__` in `tornado/httputil.py` parses the URL query string via
`parse_qs_bytes()` with no field-count limit — while the sibling POST-body parsing path
(`parse_body_arguments`) received a `max_num_fields=1000` cap added earlier in this exact
same release (v6.5.8, commit `8d6363ed`), explicitly to bound parsing cost for the identical
underlying primitive. This leaves the query-string path with the resource-exhaustion exposure
the body-path fix was meant to close.&lt;/p&gt;
&lt;p&gt;**File**: `tornado/httputil.py`, line 553 (`HTTPServerRequest.__init__`)&lt;/p&gt;
&lt;p&gt;### Root Cause&lt;/p&gt;
&lt;p&gt;```python
# tornado/httputil.py:553 (before fix)
self.arguments = parse_qs_bytes(self.query, keep_blank_values=True)
```&lt;/p&gt;
&lt;p&gt;Compare with the POST-body path fixed one commit earlier in the same release:&lt;/p&gt;
&lt;p&gt;```python
# tornado/httputil.py:1038-1041
uri_arguments = parse_qs_bytes(
    body,
    keep_blank_values=True,
    max_num_fields=config.urlencoded.max_arguments,  # default 1000
)
```&lt;/p&gt;
&lt;p&gt;Both call sites funnel through the same `tornado.escape.parse_qs_bytes` (a thin wrapper over
`urllib.parse.parse_qs`), which is exactly why `max_num_fields` was added to
`urllib.parse.parse_qsl` upstream — to let frameworks bound field count. The fix was applied
only to the body path; the query-string path was missed.&lt;/p&gt;
&lt;p&gt;The request line + headers together are capped at `max_header_size` (default 65536 bytes), so
this is not literally unbounded, but a single ~64KB request line can carry thousands of short
`key=value…&lt;/p&gt;</content:encoded>
      <guid isPermaLink="false">https://cve.radiocsirt.org/vuln/brew-snakeviz-ghsa-3hv7-mjh2-fv65</guid>
      <pubDate>Thu, 01 Oct 2026 11:45:24 +0000</pubDate>
    </item>
    <item>
      <title>BREW-snakemake-CVE-2026-100689 — GitPython submodule update path traversal can write outside the repository</title>
      <link>https://cve.radiocsirt.org/vuln/brew-snakemake-cve-2026-100689</link>
      <description>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: snakemake&lt;/p&gt;
&lt;p&gt;**Affected:** `GitPython` **3.1.61** (latest release) and `main` — `git/objects/submodule/base.py`. `git diff 3.1.61 origin/main -- git/objects/submodule/` is empty, so both are identical here.&lt;/p&gt;
&lt;p&gt;---&lt;/p&gt;
&lt;p&gt;## The gap&lt;/p&gt;
&lt;p&gt;The fix for `GHSA-hmq2-w58f-27jc` added `Submodule._validated_name()` and wired it into `update()` and five siblings, closing the `.gitmodules` **name** → `.git/modules/&amp;lt;name&amp;gt;` traversal. The other attacker-controlled `.gitmodules` field, **`path`**, is read raw:&lt;/p&gt;
&lt;p&gt;```python
# git/objects/submodule/base.py:172-177
def _set_cache_(self, attr):
    if attr in (&amp;#34;path&amp;#34;, &amp;#34;_url&amp;#34;, &amp;#34;_branch_path&amp;#34;):
        reader = self.config_reader()
        self.path = reader.get(&amp;#34;path&amp;#34;)          # raw .gitmodules value
```&lt;/p&gt;
&lt;p&gt;and GitPython&amp;#39;s own containment guard is applied in only two of the places that consume it:&lt;/p&gt;
&lt;p&gt;```
400: def _to_relative_path(cls, parent_repo, path)      # the guard (abspath + commonpath containment)
542:     path = cls._to_relative_path(repo, path)        # add()   — guarded
1041:    module_checkout_path = self._to_relative_path(self.repo, module_path)   # move() — guarded
```&lt;/p&gt;
&lt;p&gt;`update()` validates only the name and then uses the path-derived absolute location directly:&lt;/p&gt;
&lt;p&gt;```
788:  self._validated_name(self.name)                    # NAME only
801:  checkout_module_abspath = self.abspath             # derived from self.path — unguarded
821:  os.makedirs(checkout_module_abspath, exist_ok=True)
```&lt;/p&gt;
&lt;p&gt;So `path = ../../../tmp/escaped` in an attacker-authored `.gitmodules` s…&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: snakemake&lt;/p&gt;
&lt;p&gt;**Affected:** `GitPython` **3.1.61** (latest release) and `main` — `git/objects/submodule/base.py`. `git diff 3.1.61 origin/main -- git/objects/submodule/` is empty, so both are identical here.&lt;/p&gt;
&lt;p&gt;---&lt;/p&gt;
&lt;p&gt;## The gap&lt;/p&gt;
&lt;p&gt;The fix for `GHSA-hmq2-w58f-27jc` added `Submodule._validated_name()` and wired it into `update()` and five siblings, closing the `.gitmodules` **name** → `.git/modules/&amp;lt;name&amp;gt;` traversal. The other attacker-controlled `.gitmodules` field, **`path`**, is read raw:&lt;/p&gt;
&lt;p&gt;```python
# git/objects/submodule/base.py:172-177
def _set_cache_(self, attr):
    if attr in (&amp;#34;path&amp;#34;, &amp;#34;_url&amp;#34;, &amp;#34;_branch_path&amp;#34;):
        reader = self.config_reader()
        self.path = reader.get(&amp;#34;path&amp;#34;)          # raw .gitmodules value
```&lt;/p&gt;
&lt;p&gt;and GitPython&amp;#39;s own containment guard is applied in only two of the places that consume it:&lt;/p&gt;
&lt;p&gt;```
400: def _to_relative_path(cls, parent_repo, path)      # the guard (abspath + commonpath containment)
542:     path = cls._to_relative_path(repo, path)        # add()   — guarded
1041:    module_checkout_path = self._to_relative_path(self.repo, module_path)   # move() — guarded
```&lt;/p&gt;
&lt;p&gt;`update()` validates only the name and then uses the path-derived absolute location directly:&lt;/p&gt;
&lt;p&gt;```
788:  self._validated_name(self.name)                    # NAME only
801:  checkout_module_abspath = self.abspath             # derived from self.path — unguarded
821:  os.makedirs(checkout_module_abspath, exist_ok=True)
```&lt;/p&gt;
&lt;p&gt;So `path = ../../../tmp/escaped` in an attacker-authored `.gitmodules` s…&lt;/p&gt;</content:encoded>
      <guid isPermaLink="false">https://cve.radiocsirt.org/vuln/brew-snakemake-cve-2026-100689</guid>
      <pubDate>Thu, 01 Oct 2026 11:43:20 +0000</pubDate>
    </item>
    <item>
      <title>BREW-mlx-lm-CVE-2026-80047 — Hugging Face Transformers downloads custom generation code before trust consent</title>
      <link>https://cve.radiocsirt.org/vuln/brew-mlx-lm-cve-2026-80047</link>
      <description>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: mlx-lm&lt;/p&gt;
&lt;p&gt;A vulnerability in Hugging Face Transformers (versions 4.49.0, &amp;lt;= 5.8.1) allows remote Python files to be written to local disk without user consent when using GenerativePreTrainedModel.load_custom_generate(). The function fetches and caches a remote module file before performing the required trust_remote_code consent check, inverting the security model enforced by other code-loading paths (such as AutoConfig, AutoModel, and AutoTokenizer). As a result, attacker‑controlled Python code from custom_generate/generate.py is copied into the user’s ~/.cache/huggingface/modules directory even if the user declines the trust prompt. Although execution is correctly gated, the file write is not reversible and can persist across sessions. This can lead to persistent, unauthorized files on disk and stale cache collisions where cached attacker code may later be executed during trusted model loads. The issue stems from an unconditional file write in dynamic_module_utils.py prior to any trust verification.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: mlx-lm&lt;/p&gt;
&lt;p&gt;A vulnerability in Hugging Face Transformers (versions 4.49.0, &amp;lt;= 5.8.1) allows remote Python files to be written to local disk without user consent when using GenerativePreTrainedModel.load_custom_generate(). The function fetches and caches a remote module file before performing the required trust_remote_code consent check, inverting the security model enforced by other code-loading paths (such as AutoConfig, AutoModel, and AutoTokenizer). As a result, attacker‑controlled Python code from custom_generate/generate.py is copied into the user’s ~/.cache/huggingface/modules directory even if the user declines the trust prompt. Although execution is correctly gated, the file write is not reversible and can persist across sessions. This can lead to persistent, unauthorized files on disk and stale cache collisions where cached attacker code may later be executed during trusted model loads. The issue stems from an unconditional file write in dynamic_module_utils.py prior to any trust verification.&lt;/p&gt;</content:encoded>
      <guid isPermaLink="false">https://cve.radiocsirt.org/vuln/brew-mlx-lm-cve-2026-80047</guid>
      <pubDate>Thu, 01 Oct 2026 11:27:48 +0000</pubDate>
    </item>
    <item>
      <title>BREW-kimi-cli-CVE-2026-103001 — PyJWT.decode() reintroduces options-dict mutation, enabling silent claim-verification bypass on dict reuse</title>
      <link>https://cve.radiocsirt.org/vuln/brew-kimi-cli-cve-2026-103001</link>
      <description>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: kimi-cli&lt;/p&gt;
&lt;p&gt;### Summary&lt;/p&gt;
&lt;p&gt;`PyJWT.decode()`/`decode_complete()` mutates a caller-supplied `options` dict in place whenever `verify_signature` is falsy, adding `verify_exp`/`verify_nbf`/`verify_iat`/`verify_aud`/`verify_iss`/`verify_sub`/`verify_jti` keys directly onto that object. If application code reuses the same `options` dict across calls (a config object, a module-level constant, a wrapper&amp;#39;s `self.options`), a later call that explicitly sets `verify_signature=True` for a full, normal verification silently inherits the leftover `verify_exp=False` etc. from an earlier `verify_signature=False` call on the same object -- and accepts an expired, wrong-audience, wrong-issuer, or wrong-subject token as valid with no exception. Signature verification itself is unaffected; this is specifically a claims-check bypass.&lt;/p&gt;
&lt;p&gt;This is a regression of an already-fixed, closed issue: originally reported in #679 (2021), fixed in #743 (2022, via a shallow copy of `options`), and silently reintroduced by #1045 (merged 2025-03-05, a typing/logic refactor that dropped the 2022 shallow-copy line). It has been present since #1045 merged, so it likely shipped in several releases already in the wild.&lt;/p&gt;
&lt;p&gt;### Details&lt;/p&gt;
&lt;p&gt;File: `jwt/api_jwt.py`, `PyJWT._merge_options()` (called from `decode_complete()`, which backs both `jwt.decode()` and `jwt.decode_complete()`):&lt;/p&gt;
&lt;p&gt;```python
def _merge_options(self, options=None):
    if options is None:
        return self.options
    if not options.get(&amp;#34;verify_signature&amp;#34;, True):…&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: kimi-cli&lt;/p&gt;
&lt;p&gt;### Summary&lt;/p&gt;
&lt;p&gt;`PyJWT.decode()`/`decode_complete()` mutates a caller-supplied `options` dict in place whenever `verify_signature` is falsy, adding `verify_exp`/`verify_nbf`/`verify_iat`/`verify_aud`/`verify_iss`/`verify_sub`/`verify_jti` keys directly onto that object. If application code reuses the same `options` dict across calls (a config object, a module-level constant, a wrapper&amp;#39;s `self.options`), a later call that explicitly sets `verify_signature=True` for a full, normal verification silently inherits the leftover `verify_exp=False` etc. from an earlier `verify_signature=False` call on the same object -- and accepts an expired, wrong-audience, wrong-issuer, or wrong-subject token as valid with no exception. Signature verification itself is unaffected; this is specifically a claims-check bypass.&lt;/p&gt;
&lt;p&gt;This is a regression of an already-fixed, closed issue: originally reported in #679 (2021), fixed in #743 (2022, via a shallow copy of `options`), and silently reintroduced by #1045 (merged 2025-03-05, a typing/logic refactor that dropped the 2022 shallow-copy line). It has been present since #1045 merged, so it likely shipped in several releases already in the wild.&lt;/p&gt;
&lt;p&gt;### Details&lt;/p&gt;
&lt;p&gt;File: `jwt/api_jwt.py`, `PyJWT._merge_options()` (called from `decode_complete()`, which backs both `jwt.decode()` and `jwt.decode_complete()`):&lt;/p&gt;
&lt;p&gt;```python
def _merge_options(self, options=None):
    if options is None:
        return self.options
    if not options.get(&amp;#34;verify_signature&amp;#34;, True):…&lt;/p&gt;</content:encoded>
      <guid isPermaLink="false">https://cve.radiocsirt.org/vuln/brew-kimi-cli-cve-2026-103001</guid>
      <pubDate>Thu, 01 Oct 2026 11:20:23 +0000</pubDate>
    </item>
    <item>
      <title>BREW-kimi-cli-CVE-2026-100689 — GitPython submodule update path traversal can write outside the repository</title>
      <link>https://cve.radiocsirt.org/vuln/brew-kimi-cli-cve-2026-100689</link>
      <description>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: kimi-cli&lt;/p&gt;
&lt;p&gt;**Affected:** `GitPython` **3.1.61** (latest release) and `main` — `git/objects/submodule/base.py`. `git diff 3.1.61 origin/main -- git/objects/submodule/` is empty, so both are identical here.&lt;/p&gt;
&lt;p&gt;---&lt;/p&gt;
&lt;p&gt;## The gap&lt;/p&gt;
&lt;p&gt;The fix for `GHSA-hmq2-w58f-27jc` added `Submodule._validated_name()` and wired it into `update()` and five siblings, closing the `.gitmodules` **name** → `.git/modules/&amp;lt;name&amp;gt;` traversal. The other attacker-controlled `.gitmodules` field, **`path`**, is read raw:&lt;/p&gt;
&lt;p&gt;```python
# git/objects/submodule/base.py:172-177
def _set_cache_(self, attr):
    if attr in (&amp;#34;path&amp;#34;, &amp;#34;_url&amp;#34;, &amp;#34;_branch_path&amp;#34;):
        reader = self.config_reader()
        self.path = reader.get(&amp;#34;path&amp;#34;)          # raw .gitmodules value
```&lt;/p&gt;
&lt;p&gt;and GitPython&amp;#39;s own containment guard is applied in only two of the places that consume it:&lt;/p&gt;
&lt;p&gt;```
400: def _to_relative_path(cls, parent_repo, path)      # the guard (abspath + commonpath containment)
542:     path = cls._to_relative_path(repo, path)        # add()   — guarded
1041:    module_checkout_path = self._to_relative_path(self.repo, module_path)   # move() — guarded
```&lt;/p&gt;
&lt;p&gt;`update()` validates only the name and then uses the path-derived absolute location directly:&lt;/p&gt;
&lt;p&gt;```
788:  self._validated_name(self.name)                    # NAME only
801:  checkout_module_abspath = self.abspath             # derived from self.path — unguarded
821:  os.makedirs(checkout_module_abspath, exist_ok=True)
```&lt;/p&gt;
&lt;p&gt;So `path = ../../../tmp/escaped` in an attacker-authored `.gitmodules` s…&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: kimi-cli&lt;/p&gt;
&lt;p&gt;**Affected:** `GitPython` **3.1.61** (latest release) and `main` — `git/objects/submodule/base.py`. `git diff 3.1.61 origin/main -- git/objects/submodule/` is empty, so both are identical here.&lt;/p&gt;
&lt;p&gt;---&lt;/p&gt;
&lt;p&gt;## The gap&lt;/p&gt;
&lt;p&gt;The fix for `GHSA-hmq2-w58f-27jc` added `Submodule._validated_name()` and wired it into `update()` and five siblings, closing the `.gitmodules` **name** → `.git/modules/&amp;lt;name&amp;gt;` traversal. The other attacker-controlled `.gitmodules` field, **`path`**, is read raw:&lt;/p&gt;
&lt;p&gt;```python
# git/objects/submodule/base.py:172-177
def _set_cache_(self, attr):
    if attr in (&amp;#34;path&amp;#34;, &amp;#34;_url&amp;#34;, &amp;#34;_branch_path&amp;#34;):
        reader = self.config_reader()
        self.path = reader.get(&amp;#34;path&amp;#34;)          # raw .gitmodules value
```&lt;/p&gt;
&lt;p&gt;and GitPython&amp;#39;s own containment guard is applied in only two of the places that consume it:&lt;/p&gt;
&lt;p&gt;```
400: def _to_relative_path(cls, parent_repo, path)      # the guard (abspath + commonpath containment)
542:     path = cls._to_relative_path(repo, path)        # add()   — guarded
1041:    module_checkout_path = self._to_relative_path(self.repo, module_path)   # move() — guarded
```&lt;/p&gt;
&lt;p&gt;`update()` validates only the name and then uses the path-derived absolute location directly:&lt;/p&gt;
&lt;p&gt;```
788:  self._validated_name(self.name)                    # NAME only
801:  checkout_module_abspath = self.abspath             # derived from self.path — unguarded
821:  os.makedirs(checkout_module_abspath, exist_ok=True)
```&lt;/p&gt;
&lt;p&gt;So `path = ../../../tmp/escaped` in an attacker-authored `.gitmodules` s…&lt;/p&gt;</content:encoded>
      <guid isPermaLink="false">https://cve.radiocsirt.org/vuln/brew-kimi-cli-cve-2026-100689</guid>
      <pubDate>Thu, 01 Oct 2026 11:20:16 +0000</pubDate>
    </item>
    <item>
      <title>BREW-howdoi-CVE-2026-103001 — PyJWT.decode() reintroduces options-dict mutation, enabling silent claim-verification bypass on dict reuse</title>
      <link>https://cve.radiocsirt.org/vuln/brew-howdoi-cve-2026-103001</link>
      <description>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: howdoi&lt;/p&gt;
&lt;p&gt;### Summary&lt;/p&gt;
&lt;p&gt;`PyJWT.decode()`/`decode_complete()` mutates a caller-supplied `options` dict in place whenever `verify_signature` is falsy, adding `verify_exp`/`verify_nbf`/`verify_iat`/`verify_aud`/`verify_iss`/`verify_sub`/`verify_jti` keys directly onto that object. If application code reuses the same `options` dict across calls (a config object, a module-level constant, a wrapper&amp;#39;s `self.options`), a later call that explicitly sets `verify_signature=True` for a full, normal verification silently inherits the leftover `verify_exp=False` etc. from an earlier `verify_signature=False` call on the same object -- and accepts an expired, wrong-audience, wrong-issuer, or wrong-subject token as valid with no exception. Signature verification itself is unaffected; this is specifically a claims-check bypass.&lt;/p&gt;
&lt;p&gt;This is a regression of an already-fixed, closed issue: originally reported in #679 (2021), fixed in #743 (2022, via a shallow copy of `options`), and silently reintroduced by #1045 (merged 2025-03-05, a typing/logic refactor that dropped the 2022 shallow-copy line). It has been present since #1045 merged, so it likely shipped in several releases already in the wild.&lt;/p&gt;
&lt;p&gt;### Details&lt;/p&gt;
&lt;p&gt;File: `jwt/api_jwt.py`, `PyJWT._merge_options()` (called from `decode_complete()`, which backs both `jwt.decode()` and `jwt.decode_complete()`):&lt;/p&gt;
&lt;p&gt;```python
def _merge_options(self, options=None):
    if options is None:
        return self.options
    if not options.get(&amp;#34;verify_signature&amp;#34;, True):…&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: howdoi&lt;/p&gt;
&lt;p&gt;### Summary&lt;/p&gt;
&lt;p&gt;`PyJWT.decode()`/`decode_complete()` mutates a caller-supplied `options` dict in place whenever `verify_signature` is falsy, adding `verify_exp`/`verify_nbf`/`verify_iat`/`verify_aud`/`verify_iss`/`verify_sub`/`verify_jti` keys directly onto that object. If application code reuses the same `options` dict across calls (a config object, a module-level constant, a wrapper&amp;#39;s `self.options`), a later call that explicitly sets `verify_signature=True` for a full, normal verification silently inherits the leftover `verify_exp=False` etc. from an earlier `verify_signature=False` call on the same object -- and accepts an expired, wrong-audience, wrong-issuer, or wrong-subject token as valid with no exception. Signature verification itself is unaffected; this is specifically a claims-check bypass.&lt;/p&gt;
&lt;p&gt;This is a regression of an already-fixed, closed issue: originally reported in #679 (2021), fixed in #743 (2022, via a shallow copy of `options`), and silently reintroduced by #1045 (merged 2025-03-05, a typing/logic refactor that dropped the 2022 shallow-copy line). It has been present since #1045 merged, so it likely shipped in several releases already in the wild.&lt;/p&gt;
&lt;p&gt;### Details&lt;/p&gt;
&lt;p&gt;File: `jwt/api_jwt.py`, `PyJWT._merge_options()` (called from `decode_complete()`, which backs both `jwt.decode()` and `jwt.decode_complete()`):&lt;/p&gt;
&lt;p&gt;```python
def _merge_options(self, options=None):
    if options is None:
        return self.options
    if not options.get(&amp;#34;verify_signature&amp;#34;, True):…&lt;/p&gt;</content:encoded>
      <guid isPermaLink="false">https://cve.radiocsirt.org/vuln/brew-howdoi-cve-2026-103001</guid>
      <pubDate>Thu, 01 Oct 2026 11:14:16 +0000</pubDate>
    </item>
    <item>
      <title>BREW-localstack-CVE-2026-103001 — PyJWT.decode() reintroduces options-dict mutation, enabling silent claim-verification bypass on dict reuse</title>
      <link>https://cve.radiocsirt.org/vuln/brew-localstack-cve-2026-103001</link>
      <description>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: localstack&lt;/p&gt;
&lt;p&gt;### Summary&lt;/p&gt;
&lt;p&gt;`PyJWT.decode()`/`decode_complete()` mutates a caller-supplied `options` dict in place whenever `verify_signature` is falsy, adding `verify_exp`/`verify_nbf`/`verify_iat`/`verify_aud`/`verify_iss`/`verify_sub`/`verify_jti` keys directly onto that object. If application code reuses the same `options` dict across calls (a config object, a module-level constant, a wrapper&amp;#39;s `self.options`), a later call that explicitly sets `verify_signature=True` for a full, normal verification silently inherits the leftover `verify_exp=False` etc. from an earlier `verify_signature=False` call on the same object -- and accepts an expired, wrong-audience, wrong-issuer, or wrong-subject token as valid with no exception. Signature verification itself is unaffected; this is specifically a claims-check bypass.&lt;/p&gt;
&lt;p&gt;This is a regression of an already-fixed, closed issue: originally reported in #679 (2021), fixed in #743 (2022, via a shallow copy of `options`), and silently reintroduced by #1045 (merged 2025-03-05, a typing/logic refactor that dropped the 2022 shallow-copy line). It has been present since #1045 merged, so it likely shipped in several releases already in the wild.&lt;/p&gt;
&lt;p&gt;### Details&lt;/p&gt;
&lt;p&gt;File: `jwt/api_jwt.py`, `PyJWT._merge_options()` (called from `decode_complete()`, which backs both `jwt.decode()` and `jwt.decode_complete()`):&lt;/p&gt;
&lt;p&gt;```python
def _merge_options(self, options=None):
    if options is None:
        return self.options
    if not options.get(&amp;#34;verify_signature&amp;#34;, True):…&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: localstack&lt;/p&gt;
&lt;p&gt;### Summary&lt;/p&gt;
&lt;p&gt;`PyJWT.decode()`/`decode_complete()` mutates a caller-supplied `options` dict in place whenever `verify_signature` is falsy, adding `verify_exp`/`verify_nbf`/`verify_iat`/`verify_aud`/`verify_iss`/`verify_sub`/`verify_jti` keys directly onto that object. If application code reuses the same `options` dict across calls (a config object, a module-level constant, a wrapper&amp;#39;s `self.options`), a later call that explicitly sets `verify_signature=True` for a full, normal verification silently inherits the leftover `verify_exp=False` etc. from an earlier `verify_signature=False` call on the same object -- and accepts an expired, wrong-audience, wrong-issuer, or wrong-subject token as valid with no exception. Signature verification itself is unaffected; this is specifically a claims-check bypass.&lt;/p&gt;
&lt;p&gt;This is a regression of an already-fixed, closed issue: originally reported in #679 (2021), fixed in #743 (2022, via a shallow copy of `options`), and silently reintroduced by #1045 (merged 2025-03-05, a typing/logic refactor that dropped the 2022 shallow-copy line). It has been present since #1045 merged, so it likely shipped in several releases already in the wild.&lt;/p&gt;
&lt;p&gt;### Details&lt;/p&gt;
&lt;p&gt;File: `jwt/api_jwt.py`, `PyJWT._merge_options()` (called from `decode_complete()`, which backs both `jwt.decode()` and `jwt.decode_complete()`):&lt;/p&gt;
&lt;p&gt;```python
def _merge_options(self, options=None):
    if options is None:
        return self.options
    if not options.get(&amp;#34;verify_signature&amp;#34;, True):…&lt;/p&gt;</content:encoded>
      <guid isPermaLink="false">https://cve.radiocsirt.org/vuln/brew-localstack-cve-2026-103001</guid>
      <pubDate>Thu, 01 Oct 2026 11:14:06 +0000</pubDate>
    </item>
    <item>
      <title>BREW-livereload-GHSA-chx6-46f5-w4vp — tornado: CurlAsyncHTTPClient enforces no response-size limit — decompression bomb drives unbounded memory accumulation…</title>
      <link>https://cve.radiocsirt.org/vuln/brew-livereload-ghsa-chx6-46f5-w4vp</link>
      <description>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: livereload&lt;/p&gt;
&lt;p&gt;An unbounded memory accumulation (decompression bomb) in `tornado.curl_httpclient.CurlAsyncHTTPClient` — the client-side sibling gap of CVE-2026-49855 — verified end-to-end on the 2026-08-15 master snapshot (`6.6.dev1`) and present unchanged in the latest release tag `v6.5.8` and on master (checked 2026-08-17). When a Tornado application configures the curl client (the documented deployment for proxy support / advanced TLS options) and `fetch()`es an attacker-chosen or attacker-compromised URL with default `decompress_response=True`, a malicious server replying `Content-Encoding: gzip` with a ~2.8 MB wire bomb drove the client&amp;#39;s RSS from 30,884 kB to **1,032,100 kB (~1008 MB) in 3.18 s** (~350 MB/s, monotonic, no plateau) until the kernel OOM-killed the process (exit 137, cgroup `OOMKilled=true`) — with the transfer only 67% complete and **no client-side size check ever intervening**: `curl_httpclient.py` contains zero occurrences of `max_body_size`/`MAXFILESIZE`. The identical bomb against the default `SimpleAsyncHTTPClient` fails cleanly at ~65 MB, because every size gate CVE-2026-49855 added (compressed CL, chunked total, cumulative decompressed size — `http1connection.py:620,676,742`) lives in code the curl client never executes. This is a distinct component from the published advisory (which fixed `_GzipMessageDelegate`/`SimpleAsyncHTTPClient` only) and from the other curl-client advisories (credential handle-reuse GHSA-pw6j-qg29-8w7f, header CRLF GHSA-w235-7p84-xx57);…&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Affected:&lt;/strong&gt; Homebrew: livereload&lt;/p&gt;
&lt;p&gt;An unbounded memory accumulation (decompression bomb) in `tornado.curl_httpclient.CurlAsyncHTTPClient` — the client-side sibling gap of CVE-2026-49855 — verified end-to-end on the 2026-08-15 master snapshot (`6.6.dev1`) and present unchanged in the latest release tag `v6.5.8` and on master (checked 2026-08-17). When a Tornado application configures the curl client (the documented deployment for proxy support / advanced TLS options) and `fetch()`es an attacker-chosen or attacker-compromised URL with default `decompress_response=True`, a malicious server replying `Content-Encoding: gzip` with a ~2.8 MB wire bomb drove the client&amp;#39;s RSS from 30,884 kB to **1,032,100 kB (~1008 MB) in 3.18 s** (~350 MB/s, monotonic, no plateau) until the kernel OOM-killed the process (exit 137, cgroup `OOMKilled=true`) — with the transfer only 67% complete and **no client-side size check ever intervening**: `curl_httpclient.py` contains zero occurrences of `max_body_size`/`MAXFILESIZE`. The identical bomb against the default `SimpleAsyncHTTPClient` fails cleanly at ~65 MB, because every size gate CVE-2026-49855 added (compressed CL, chunked total, cumulative decompressed size — `http1connection.py:620,676,742`) lives in code the curl client never executes. This is a distinct component from the published advisory (which fixed `_GzipMessageDelegate`/`SimpleAsyncHTTPClient` only) and from the other curl-client advisories (credential handle-reuse GHSA-pw6j-qg29-8w7f, header CRLF GHSA-w235-7p84-xx57);…&lt;/p&gt;</content:encoded>
      <guid isPermaLink="false">https://cve.radiocsirt.org/vuln/brew-livereload-ghsa-chx6-46f5-w4vp</guid>
      <pubDate>Thu, 01 Oct 2026 11:13:05 +0000</pubDate>
    </item>
  </channel>
</rss>
