### Summary
GitPython's `Actor.name_email_regex` regular expression (`git/util.py`, line 863)
is vulnerable to catastrophic backtracking (ReDoS — Regular Expression Denial of
Service). When GitPython parses the `author` or `committer` header of a git commit
object that contains a long string with an unterminated `<` (no matching `>`), the
Python regex engine enters quadratic backtracking, causing complete single-threaded
CPU exhaustion proportional to the square of the input length.
A single crafted commit object can block any GitPython API call that reads
`.author` or `.committer` for **over two minutes per invocation**, enabling denial
of service against CI runners, code-hosting backends, repository-scanning
pipelines, or any service that processes commits from third-party or untrusted
repositories.
---
### Details
**Vulnerable file and line:**
`git/util.py`, line 863:
```python
name_email_regex = re.compile(r"(.*) <(.*?)>")
```
This regex is evaluated inside `Actor._from_string()` (line 909) every time
GitPython resolves a commit's `.author` or `.committer` property.
**Full call chain — from public API to vulnerable sink:**
commit.author # any ordinary GitPython API call
└── git/objects/commit.py:917
Commit._deserialize()
└── git/objects/util.py:341
parse_actor_and_date(author_line)
└── git/util.py:909
Actor._from_string(string)
└── Actor.name_email_regex.search(string) ← VULNERABLE
`author_line` is decoded directly from the raw bytes of the git commit object with
**no length limit, character restriction, or timeout** applied at any point before
reaching the regex engine. The same chain is triggered by:
- `commit.author`
- `commit.committer`
- `repo.iter_commits()`
- `repo.blame()`
- Any web service / CI tool that displays or processes commit metadata
**Why this pattern backtracks catastrophically:**
The pattern `(.*) <(.*?)>` contains an unbounded greedy group `(.*)` followed by
a literal space and `<`. When the input is a long string that contains `<` but no
closing `>`, the regex engine must try every possible split position for the greedy
group — O(n²) candidate positions for a string of length n — each of which then
drives the inner lazy group into further sub-match attempts. This is the
well-documented "catastrophic backtracking" failure mode for this family of
patterns.
**Empirically measured scaling (tested against GitPython 3.1.59, commit 52a6cba):**
| Author field length (bytes) | Time to resolve `.author` |
|-----------------------------|--------------------------|
| 1,000 | 0.0035 s |
| 5,000 | 0.084 s |
| 10,000 | 0.341 s |
| 20,000 | 1.525 s |
| 40,000 | 5.963 s |
| 60,000 | 13.360 s |
| 80,000 | 23.871 s |
| **200,000** | **150.488 s** |
Each doubling of input size roughly quadruples processing time (e.g. 40,000 →
80,000 bytes: 5.96 s → 23.87 s ≈ 4.0×), confirming O(n²) growth. Git itself
imposes **no practical size limit** on author name fields in the object format.
**How the malicious object reaches a victim:**
The PoC creates the commit object as a correctly SHA-1-hashed, zlib-compressed git
loose object written directly into `.git/objects/`. `git cat-file -t <sha>` confirms
it is a valid `commit` type and git's own read-side tools display it without error.
Only git's write-side tooling (`git commit --author`, `git update-index`,
explicit `git fsck`) applies the sanity checks that would reject a malformed author
line. Delivery paths that bypass those checks include:
- A git server with `receive.fsckObjects = false` (common in self-hosted deployments)
- A `.git` directory shipped as a tarball, backup, or zip archive
- A git bundle file
- Any automated mirror or import tool that operates at the object level
---
### PoC
**Environment used for testing:**
- GitPython 3.1.59, installed in editable mode from source (no code modifications)
- Python 3.12.3, git 2.43.0, Ubuntu 24.04
**Script 1 — craft the malicious repository (`craft_malicious_repo.py`):**
```python
#!/usr/bin/env python3
"""
Creates a git repository with one commit whose 'author' field is a large
string containing an unterminated '<'. Bypasses git's write-side sanity
checks by writing the raw object directly into .git/objects/.
Usage: python3 craft_malicious_repo.py <target_dir> <payload_size_bytes>
"""
import hashlib, os, subprocess, sys, zlib
def run(cmd, cwd):
return subprocess.run(cmd, cwd=cwd, check=True, capture_output=True, text=True)
def main():
if len(sys.argv) != 3:
print(f"usage: {sys.argv[0]} <target_dir> <payload_size_bytes>")
sys.exit(1)
target_dir, payload_size = sys.argv[1], int(sys.argv[2])
os.makedirs(target_dir, exist_ok=True)
run(["git", "init", "--quiet"], cwd=target_dir)
run(["git", "config", "user.email", "poc@example.com"], cwd=target_dir)
run(["git", "config", "user.name", "PoC"], cwd=target_dir)
with open(os.path.join(target_dir, "README.txt"), "w") as f:
f.write("GitPython ReDoS PoC repository\n")
run(
### Summary
`Repo.__init__` decides which directory is the git directory by testing candidate paths in an order that
considers the real `.git` **last**. Two earlier tests can be satisfied by ordinary tracked files. Git
reserves only the literal name `.git`, so `HEAD`, `objects/`, `refs/`, `config`, `gitdir`, `commondir`
and `hooks/` at a repository root are all legal tracked content.
Consequently, after a victim opens or clones an attacker's repository, GitPython resolves `git_dir` to
the **working-tree root** while real git correctly resolves `<root>/.git`. Everything GitPython then
treats as "inside the git directory" is attacker-authored content — including `hooks/`, which it
executes.
### CVE-2026-87817
### Affected code (3.1.59)
The discovery loop in `git/repo/base.py` tests, in order:
1. `git/repo/base.py:299` — `isfile(curpath/gitdir)` **and** `isfile(curpath/commondir)` **and** `isfile(curpath/HEAD)`
2. `git/repo/base.py:320` — `is_git_dir(curpath)`
3. `git/repo/base.py:341` — `dotgit = osp.join(curpath, ".git")` ← the real git dir, considered last
`is_git_dir` (`git/repo/fun.py:60`) requires only that `objects/` and `refs/` are directories and that
`HEAD` is a file; **`HEAD`'s contents are never parsed.** The hook path is resolved from
`index.repo.git_dir` (`git/index/fun.py:73`), i.e. the mis-resolved directory.
### Proof of concept
Requires only `pip install GitPython==3.1.59`. Full script attached as `poc1_rce.py`; it runs entirely
in a temp directory and the payload only writes a marker file.
Attacker repository — four ordinary tracked files at the root:
| path | mode | content |
|---|---|---|
| `gitdir` | 100644 | `.git\n` |
| `commondir` | 100644 | `.git\n` |
| `HEAD` | 100644 | `ref: refs/heads/master\n` |
| `hooks/pre-commit` | **100755** | `#!/bin/sh` + payload |
Victim — two ordinary calls:
```python
repo = git.Repo.clone_from(url, dst) # or git.Repo(dst)
repo.index.commit("automated commit") # code execution happens here
```
Observed on the PyPI release 3.1.59 (Linux and Windows):
```
real git says the git dir is : /tmp/.../victim/.git
GitPython says it is : /tmp/.../victim <- shadowed
attacker's hook executed : True
git fsck : (clean)
```
The `pre-commit` hook runs at `git/index/base.py:1201`, before `write_tree()`, so it fires even though
the commit later fails.
### Impact
A service that opens or clones an untrusted repository with GitPython — a CI runner building a fork
pull request, a code-scanning/SBOM service, a mirror, a dependency bot, an AI code-review/agent tool —
can be made to:
1. **Execute arbitrary commands** via the tracked `<root>/hooks/pre-commit` when the victim calls
`index.commit()`.
2. **Read files outside the repository**: the tracked `<root>/config` becomes the repository config and
is parsed with `merge_includes=True` (`git/repo/base.py:765`), so `[include] path = ~/.aws/credentials`
discloses the file. (`Repo._config_reader` still defaults `merge_includes=True`, so the hardening
added in 3.1.59 for `.gitmodules`/GHSA-7833 does not cover this path.)
3. **Write a config file to an attacker-chosen directory** via an absolute tracked `commondir`.
Delivery is silent: `git clone` exits 0, `git fsck` (including `--strict`, and with
`transfer.fsckObjects`/`fetch.fsckObjects=true`) reports nothing, and the clone passes every read-only
probe (`head.commit`, `branches`, `is_dirty()`, `untracked_files`, `iter_commits`) because a tracked
`commondir` of `.git` pins `common_dir` to the real `.git`.
### Threat model / preconditions
- Attacker controls the content of a repository the victim opens or clones with GitPython (public repo,
fork PR, mirrored dependency).
- For code execution, the victim performs a commit via GitPython's native `index.commit()`. For the
file-read impact, opening the repo and reading config is enough.
- `GIT_WORK_TREE` is **not** a mitigation — `git_dir` is assigned and the discovery loop breaks before
the environment is consulted.
- Real git is unaffected; only GitPython mis-resolves the directory.
### Remediation
1. Test `curpath/.git` **before** the `gitdir`/`commondir`/`HEAD` triple and before `is_git_dir(curpath)`.
2. Resolve `hook_path` / `_commit_hook_path` / `_get_validated_reflog_path` through `repo.common_dir`.
3. Containment-check `commondir`/`gitdir` contents before joining (reuse
`SymbolicReference._get_validated_path`).
4. Validate `HEAD` in `is_git_dir` (require `ref: refs/...` or a 40-hex sha).
5. Pass `merge_includes=False` in `Repo._config_reader` (`git/repo/base.py:765`).
### Note for the maintainers
Commit `406b98e1` (2026-05-31, "respect core.hooksPath for commit hooks") resolved the hook path via
`git rev-parse --git-path`, which is immune because git rediscovers the true `.git`. Commit `9bc287a2`
(2026-07-20) reverted it to avoid an unconditional `rev-parse` dependency — reintroducing the exec step.
Measured at each commit with the healthy-lo
### Impact
Services using internationalized (non-ASCII) domain names are potentially vulnerable to TLS connections made from AnyIO's `connect_tcp()` or directly via `TLSStream.wrap()` where the connection has (through other means) been hijacked and redirected to a malicious server. The attacker would obtain a legitimate certificate using the IDNA 2003 encoded version of the domain name and offer it to the connecting client, making the certificate validate properly on the client's end.
### Patches
The vulnerability will be patched in v4.14.2.
### Workarounds
Encode host names via the `idna` package prior to connecting.
GitPython 3.1.59 contains a vulnerability in its high-level diff API where the --no-index option bypasses protections against unsafe git diff options. This allows an attacker who can influence diff options and paths to use a blind local-file content oracle to infer contents of local files accessible to the GitPython process. The issue arises because --no-index treats path arguments as arbitrary filesystem paths, and when combined with the -I/--ignore-matching-lines option, it creates a content-dependent Boolean oracle. This vulnerability was confirmed in version 3.1.59 and is fixed in version 9.26.0 of the affected product, snakemake. Exploitation requires the embedding application to expose success/error distinctions and allow repeated queries with attacker-controlled inputs.
AnyIO versions prior to 4.14.2 contain a resource allocation vulnerability where process-pool workers' standard error pipes are not drained by the parent process. This can cause the pipe to fill and block the worker if it writes enough data to stderr, leading to indefinite blocking of the process-pool call. The issue affects applications running untrusted or faulty worker code that produces substantial standard error output. The vulnerability is fixed in AnyIO version 4.14.2.
CVE-2026-63349 is a privilege assignment vulnerability in AnyIO versions 4.14.0 up to but not including 4.14.2. The issue arises because anyio.open_process() incorrectly forwards the extra_groups argument, causing child processes to retain parent supplementary groups instead of properly clearing or setting them. This affects POSIX privilege dropping correctness in applications relying on AnyIO subprocess helpers. The flaw can also cause a TypeError if conflicting arguments are supplied.
CVE-2026-85999 is a medium severity vulnerability in the Python library soupsieve (used by BeautifulSoup) that causes uncontrolled resource consumption (quadratic time complexity) when parsing certain valid CSS selectors containing long runs of whitespace or comments. This happens due to an inefficient regular expression search that triggers excessive backtracking, leading to significant CPU usage and interpreter stalls. The issue affects soupsieve versions prior to 2.9. Exploitation requires user-controlled CSS selectors, so applications using only hard-coded selectors are not affected.
CVE-2026-86000 is a medium severity vulnerability in the Python library soupsieve (used by BeautifulSoup) involving uncontrolled resource consumption due to a regular expression backtracking issue. The flaw arises from overlapping quantified character classes in the regex pattern used to parse CSS selectors, causing quadratic time complexity when processing certain crafted selectors that fail to match. This can lead to significant CPU consumption and delays when user-controlled selectors are processed. The vulnerability affects soupsieve versions prior to 2.9. Exploitation requires passing a maliciously crafted CSS selector string to soupsieve or BeautifulSoup's select methods. No known exploits are reported in the wild, and no official patch information is provided in the input data.
CVE-2026-69243 is a high-severity vulnerability in the python-aiohttp library involving HTTP request smuggling related to WebSocket upgrade requests. It affects multiple specific versions of python-aiohttp, including versions from 1.16.2 up to but not including 1.16.51_2, as well as several other discrete versions. Red Hat has issued an important security advisory and provides a patched container image for Red Hat Lightspeed in Satellite to mitigate this issue. No known exploits have been reported in the wild. The vulnerability relates to local system health analysis without external data transmission.
### Impact
An OpenAPI or AsyncAPI description could make the `split` command write
files outside the chosen output directory, on the machine of anyone who runs
`split` against it. The write is constrained rather than a free file-write
primitive: component data is emitted only as YAML/JSON, and code-sample files are
named after the HTTP method, so an attacker can place or overwrite files at an
unintended path but has limited control over their name and contents.
### Patches
Fixed in `@redocly/cli` v2.33.2.
### Workarounds
Do not run the `split` command on API descriptions from untrusted or unreviewed sources.
To detect an exploit attempt, inspect the description's component names and
`x-codeSamples` `lang` values for a literal `../` — neither legitimately contains
path segments.
CVE-2026-59823 is a Server-Side Request Forgery (SSRF) vulnerability in BerriAI's LiteLLM proxy server versions prior to 1.83.9. An authenticated user with a valid virtual key could exploit this flaw by placing an api_base parameter inside the user_config request body, bypassing existing safety checks. This allows the attacker to redirect server-side requests to internal or external hosts not normally accessible, potentially exposing sensitive endpoints. The issue is resolved in version 1.83.9.
Tornado versions before 6.5.7 contain a vulnerability in CurlAsyncHTTPClient where pycurl handles are reused across requests without properly clearing state. This can lead to leakage of sensitive credentials such as TLS certificates or proxy authentication data across unintended requests. The vulnerability has a medium severity with a CVSS score of 5.9. Multiple specific Tornado package versions are affected. No explicit patch information is provided in the source data.
Tornado versions from 6.5.5 up to but not including 6.5.8 contain an incomplete fix for cookie attribute injection. This vulnerability allows attackers to inject arbitrary cookie attributes by using capitalized or legacy keyword arguments in the set_cookie function. By embedding semicolon-delimited data in parameters such as Domain, Path, or SameSite, attackers can bypass validation and alter cookie security attributes. The issue is classified as an HTTP request/response splitting vulnerability related to improper neutralization of CRLF sequences in HTTP headers.
CVE-2026-91990 is a high-severity denial-of-service vulnerability in the Tornado web framework affecting versions from 0 up to but not including 6.5.8. The vulnerability arises from the multipart form data parsing function, which creates a large transient list by splitting the entire request body before enforcing limits on the number of parts. This can lead to excessive memory consumption and a denial of service when processing specially crafted HTTP requests with many parts. The vulnerability is exploitable without authentication.
Tornado's CurlAsyncHTTPClient class is vulnerable to HTTP request/response splitting due to improper neutralization of CRLF sequences in HTTP headers. This vulnerability allows attackers to inject arbitrary headers or additional HTTP requests when untrusted header values are used. It affects Tornado versions prior to 6.4.1 and has a medium severity rating.
CVE-2024-14029 is a critical HTTP request smuggling vulnerability in Tornado web server versions prior to 6.4.1. It occurs when Tornado receives HTTP requests containing two Transfer-Encoding: chunked headers, causing it to ignore both and misinterpret the request body. This behavior can lead to request smuggling attacks when Tornado is deployed behind proxies that forward such malformed requests, such as Pound. The vulnerability may allow attackers to bypass access controls, poison caches, or cause connection desynchronization.
## Summary
`TagReference.create()` forwards a caller-influenced positional `reference` value into `git tag` without it ever being inspected by the unsafe-option guard, allowing an arbitrary file read (the file's contents are returned in-band as the annotated tag message). This is an incomplete-fix bypass of commit `3af0c251` (the fix for GHSA-3f7w-8rr8-f37f's tag instance).
## Root Cause
The fix `3af0c251` added `unsafe_git_tag_options = ["--file","-F"]` and a guard call, but the guard is `Git.check_unsafe_options(options=Git._option_candidates([], kwargs), unsafe_options=...)` at `git/refs/tag.py:139` — it passes an EMPTY args list and inspects **kwargs only**. The dangerous values `path` and `reference` are POSITIONALS (`args = (path, reference)`, tag.py:156), placed before any `--`. A user-influenced `reference="--file=<path>"` therefore reaches `git tag` as the exact `--file` option the fix intended to block, creating an annotated tag whose message is the file's contents.
## Impact
Arbitrary local file read at the privileges of the host process; contents returned in-band via `tagref.tag.message`. Requires the embedding application to forward a caller-influenced `reference` value into `TagReference.create()` (pure VALUE control — the CVE-2026-42215 threat model). Default `allow_unsafe_options=False`.
## Proof of Concept
```python
from git import TagReference
t = TagReference.create(repo, "vpwn", reference="--file=/home/app/.ssh/id_rsa")
print(t.tag.message) # contents of the file
```
## Attack Chain
1. Entry: app calls `TagReference.create(repo, name, reference=<user>)` with `reference="--file=/home/app/.ssh/id_rsa"`.
2. Check: `Git.check_unsafe_options(_option_candidates([], kwargs), ["--file","-F"])` @ tag.py:137-141. Guard: denylist includes `--file`/`-F`. Bypass proof: `_option_candidates` receives `args=[]` → the positional `reference` is never a candidate (the kwarg spelling `file="…"` IS blocked; only the positional escapes).
3. Sink: `repo.git.tag(*args, **kwargs)` @ tag.py:158 → no `--`. argv (observed): `['git','tag','-f','vpwn','--file=<secret>']`.
4. Impact: annotated tag created; `tagref.tag.message` == file contents (arbitrary file read).
## Bypass Evidence
Independently reproduced (independent test harness, git 2.43.0, default `allow_unsafe_options=False`): `TagReference.create(repo,'vp','--file=<secret>')` → PASSED; `tag.message == 'GATE_SECRET_LINE_A\nGATE_SECRET_LINE_B'`. Control: `TagReference.create(..., file='<secret>')` → `UnsafeOptionError: --file is not allowed`. Fix-commit read: `3af0c251` adds `_option_candidates([], kwargs)` (empty args → positional never a candidate).
## Affected Versions
`GitPython <= 3.1.58` (sink present verbatim on the latest release tag; `git diff 3.1.57..HEAD` touches only test files).
## Suggested Fix
Include the positional `reference` (and `path`) in the option-candidate list passed to `check_unsafe_options`, or place a `--` separator before the positional arguments in `TagReference.create()`.
---
Reported by **zx (Jace)** — GitHub: @manus-use
A Server-Side Request Forgery (SSRF) vulnerability exists in nltk/nltk versions 3.9.4 and the current develop branch. The `nltk.pathsec.validate_network_url()` function, intended to prevent SSRF by rejecting internal network addresses, fails to reject IPs in the RFC 6598 shared address space (`100.64.0.0/10`). This occurs because Python's `ipaddress` module does not classify such addresses as `is_private` or `is_global`, and the current guard only checks `is_private` and a few explicit categories. An attacker who can influence a URL passed to NLTK's network-loading helpers can exploit this vulnerability to make a strict-mode application send requests to shared-address-space hosts, potentially exposing non-public infrastructure reachable from the application host. The impact is limited to SSRF-style confidentiality exposure, with no code execution claimed.