Threats affecting pkg:bitnami/brotli
191,086 threats
Most recently published first
| Severity | CVSS | Signals | Threat | Source | Published | Score and star |
|---|---|---|---|---|---|---|
| Critical | 9.2 | EPSS 0.5% | CVE-2026-110858Dvc: GitPython Remote.pull() refspec bypasses the --upload-pack unsafe-option guard, enabling arbitrary command Critical9.2EPSS 0.5%GCVE DatabaseCVE-2026-110858 ## Summary `Remote.pull()` in GitPython forwards its positional `refspec` argument directly to the `git pull` child process while its unsafe-option guard inspects only keyword arguments. An application that passes attacker-influenced data as the `refspec` (for example a user-supplied branch or refspec parameter in a web or CI service) can supply an option-shaped value such as `--upload-pack=<command>`. The value bypasses the `unsafe_git_pull_options` check, is placed after the `--` separator on the `git pull` command line, and is executed as the upload-pack program for local-path or SSH transports. This yields arbitrary command execution with the privileges of the calling process using the documented default arguments (`allow_unsafe_options=False`). ## Root Cause `Remote.pull()` is documented to reject dangerous options unless `allow_unsafe_options=True`, and `--upload-pack` is explicitly listed as such an option. The guard, however, only screens keyword arguments and never the positional `refspec`. The check builds its candidate list with `Git._option_candidates([], kwargs)`, passing an empty list in place of the positionals, so the `refspec` value is never compared against `unsafe_git_pull_options = ["--upload-pack"]`. The normalized `refspec` is then forwarded verbatim to the `git pull` invocation. The leading `--` separator that neutralizes an option-shaped refspec for `git fetch` and `git push` does not protect `git pull`, because `git pull` re-forwards its trailing positionals to an internal `git fetch` without re-inserting a `--` separator, so the positional becomes an option to that internal fetch. ```text Affected function: git.remote.Remote.pull Source: git/remote.py (guard at the check_unsafe_options call using _option_candidates([], kwargs); sink at the self.repo.git.pull("--", self, refspec, ...) invocation) ``` ## Impact An attacker who controls the `refspec` value passed to `Remote.pull()` achieves arbitrary operating-system command execution as the process invoking GitPython, with no additional privileges and no user interaction, on the default code path. Exploitation requires that the pulled remote uses a transport that runs upload-pack on the local side, namely a local filesystem path or SSH; over HTTPS the `--upload-pack` value is inert because the server ignores a client-supplied upload-pack program. This transport requirement narrows the population of vulnerable deployments but does not add work for the attacker once a vulnerable target is reached. Working checkouts commonly use local-path or SSH origins, so the exposed population is substantial. ## Proof of Concept ```text Environment: GitPython from the affected source tree; system git 2.43.0. The pulled origin uses a local filesystem transport (protocol.file.allow=always for git >= 2.38). ``` Reproduction steps: 1. Create an origin repository reachable over a local-path transport. 2. In a consumer repository, register that origin and call `Remote.pull()` with an attacker-controlled refspec. 3. Observe that the injected command executes. ```python import git repo = git.Repo("consumer") # has an origin using a local-path or ssh transport origin = repo.remote("origin") # Attacker-controlled refspec value; default arguments (allow_unsafe_options=False): origin.pull(refspec="--upload-pack=touch /tmp/pwned; git-upload-pack") ``` ```text cmdline: git pull -v -- origin --upload-pack=touch /tmp/pwned; git-upload-pack [attack] marker created (RCE) : True [isolate] upload_pack passed as KWARG blocked : True (only the positional refspec escapes) [sibling] same value via git fetch : safe (pull-specific behavior) [control] benign refspec : safe ``` ## Attack Chain 1. **Exposure:** An application calls `Remote.pull(refspec=<value>)` on the default path, where `<value>` is derived from attacker-influenced input such as a branch or refspec request parameter. 2. **Control:** The attacker supplies the string `--upload-pack=<command>; git-upload-pack`. The value passes through `Git._unpack_args`, which only flattens and stringifies it, preserving the option shape. 3. **Path:** The protocol loop calls `Git.check_unsafe_protocols` on the value, which only matches remote-helper transport URLs (a `<transport>::` form) and does not flag an `--upload-pack=` token. 4. **Guard:** The unsafe-option guard calls `check_unsafe_options(options=Git._option_candidates([], kwargs), ...)`. Because positionals are passed as an empty list, the positional `refspec` is never inspected against `unsafe_git_pull_options`. 5. **Primitive:** The value is forwarded to `git pull -v -- <remote> --upload-pack=<command>; git-upload-pack`; `git pull` re-forwards the positional to an internal `git fetch` without a `--` separator, so it is honored as the upload-pack program. 6. **Result:** For a local-path or SSH transport, git executes `<command>` as the upload-pack program, giving arbitrary command execution as the calling p | GCVE Database | ||
| High | 7.7 | EPSS 0.4% | High7.7EPSS 0.4%GCVE DatabaseCVE-2026-110857 ### Summary GitPython has spent 2022-2026 rolling out two reusable guards method-by-method: `Git.check_unsafe_protocols(url)` (raises `UnsafeProtocolError` for `<transport>::` remote-helper URLs - CVE-2022-24439) and `Git.check_unsafe_options(...)` (raises `UnsafeOptionError` for denylisted option spellings - CVE-2023-40267, CVE-2026-42215, CVE-2026-67323). Three wrapper methods that build a `git` argv from caller-controlled values still have **neither**: 1. **`Git.ls_remote`** (git/cmd.py:1128-1143) - checks options (:1139-1141) but forwards the URL positional unchecked; no `allow_unsafe_protocols` parameter exists. The same URL through `clone_from` raises `UnsafeProtocolError`. 2. **`Repo.merge_base`** (git/repo/base.py:895-936) - forwards `**kwargs`/`*rev` to `self.git.merge_base` with no guard; guarded sibling `iter_commits` sits 12 lines up. Its exception handler additionally swallows every non-128 `GitCommandError` into `[]`, masking the injection. 3. **`IndexFile.move`** (git/index/base.py:1092-1168) - forwards `**kwargs` to `git mv` twice (:1147, :1167); guarded sibling `remove()` is 23 lines above. Verified unfixed on `main @ a54d159` (2026-09-21), post-dating latest release 3.1.62 (2026-09-07) - all three sites re-verified today. ### Impact **Site 1 (`ls_remote`)** - RCE conditional on the victim git transport policy. The common 'probe first, then clone' pattern (`Git(...).ls_remote(attacker_url)`) with an `ext::sh -c ...` URL executes arbitrary commands when the victim git permits the ext transport: dynamically verified with `protocol.ext.allow=always` (marker executed). git >= 2.38.1 stock config refuses the transport (`fatal: transport 'ext' not allowed`, rc 128) - GitPython adds no defense-in-depth and surfaces a bare `GitCommandError`. Environments on git < 2.38.1 are exposed with stock config - the exact population CVE-2022-24439 was scored against. **Sites 2/3** - latent argument-injection surface: caller kwargs flow 1:1 into the `git` argv before operands and `--` (cmd.py:1850 `opt_args + ext_args`), and `execute_kwargs` (incl. `shell=True`/`env` - the CVE-2023-40267 Windows mechanism) passes through unguarded. Today `git merge-base`/`git mv` expose no exec option, so we claim the missing-guard class (vendor-settled by CVE-2026-67323, which bundled archive/ls_remote/iter_commits/blame in one advisory), not direct RCE. ### Dynamic verification (today; GitPython main @ a54d159, git 2.55.0, Linux) | # | Call | Result | |---|---|---| | T1 | `repo.git.ls_remote('ext::/tmp/gp_touch.sh')`, stock config | git refuses (rc 128); **no `UnsafeProtocolError` from GitPython** | | T1b | same + victim config `protocol.ext.allow=always` | **marker created - arbitrary command executed** | | T2 | `repo.git.ls_remote('.', upload_pack=...)` | blocked by cmd.py:1141 (options-side fixed - protocol-side is the residual gap) | | T3 | `repo.merge_base('main','side', upload_pack=...)` | **not blocked**; argv `git merge-base --upload-pack=... main side`; rc 129 silently converted to `[]` | | T4 | `repo.index.move(['f1','f2'], output=...)` | **not blocked**; option reaches `git mv` (rc 129) | ### Coverage matrix (main @ a54d159) `check_unsafe_protocols`: 7 call sites (`_clone`, `archive`, `set_url`, `create`, `fetch`, `pull`, `push`) - none on `ls_remote`. `check_unsafe_options`: 24 call sites incl. `ls_remote`, `iter_commits`, `IndexFile.remove` - absent on `merge_base` and `move`. ### Precedents (same repo, same guard family) - **CVE-2022-24439 / GHSA-hcpj-qp55-gfph** (critical): `check_unsafe_protocols` on the clone path - Site 1 is the same `ext::` primitive on a sibling API. - **CVE-2023-40267 / GHSA-pr76-5cm5-w9cj** (critical, 'incomplete fix for CVE-2022-24439'): `check_unsafe_options` - unguarded kwargs to argv treated as RCE (`shell=True` on Windows). - **CVE-2026-42215 / GHSA-rpm5-65cw-6hj4** (high, fixed 3.1.47): underscored-spelling bypass - rollout still active in 2026. - **CVE-2026-67323 / GHSA-956x-8gvw-wg5v** (high, 8.4, fixed 3.1.51): added `check_unsafe_options` to `archive`/`ls_remote`/`iter_commits`/`blame`; its PoC is literally `repo.git.ls_remote('.', upload_pack=...)`. Establishes that a wrapper missing the check is itself a vulnerability. This report is the residual gap of that rollout: `ls_remote` got options but not protocols; `merge_base`/`move` got neither. ### Recommended fix (existing helpers, 1-3 lines per site) 1. `Git.ls_remote`: `if not allow_unsafe_protocols: self.check_unsafe_protocols(args[0])` + new `allow_unsafe_protocols: bool = False` (the `_clone` pattern, base.py:1536-1538). 2. `Repo.merge_base`: `check_unsafe_options(Git._option_candidates(list(rev), kwargs), unsafe_options=self.unsafe_git_revision_options)` (the `iter_commits` pattern); stop masking non-128 statuses. 3. `IndexFile.move`: the `remove()` pattern with `unsafe_git_pathspec_from_file_options`. One advisory, three affected methods, severity anchored on Site 1 - mirroring CVE-2026-67323's bundling. | GCVE Database | ||
| High | 8.7 | EPSS 0.3% | CVE-2026-110856Dvc: GitPython parse_actor_and_date() is vulnerable to quadratic backtracking on malformed identity lines High8.7EPSS 0.3%GCVE DatabaseCVE-2026-110856 ### Summary This is a residual of GHSA-g5vv-9gxw-82hx / CVE-2026-87819. The 3.1.60 fix made `Actor._from_string` regex-free, but `parse_actor_and_date` still runs `_re_actor_epoch` on the same author/committer/tagger line one step earlier. That pattern backtracks quadratically when the trailing epoch or timezone is missing or malformed, so the same class of slow parse is still reachable in 3.1.62. ### Details `git/objects/util.py:322`: ```python _re_actor_epoch = re.compile(r"^.+? (.*) (\d+) ([+-]\d+).*$") ``` `.+?` and `(.*)` can both absorb the same run of spaces, and the required ` (\d+) ([+-]\d+)` tail is only checked after them. On a long line with no valid tail, the search retries every split point. It is called from `parse_actor_and_date` (`git/objects/util.py:335`), which runs on the author and committer lines in `Commit._deserialize` (`git/objects/commit.py:917`, `:931`) and on the tagger line in `git/objects/tag.py:129`. The published payload (letters plus an unterminated `<`) is fast on 3.1.62. Replacing the letters with spaces is not: | author line (after `author `) | 2 KB | 8 KB | 32 KB | |---|---|---|---| | `'a'*n + ' <'` (advisory payload) | 0.0 ms | 0.1 ms | 0.4 ms | | `' '*n` | 28 ms | 453 ms | 7,700 ms | | valid line, long name (control) | 0.0 ms | 0.0 ms | 0.0 ms | Cost grows about 16x for each 4x increase in length. ### PoC ```python import time from git.objects.util import parse_actor_and_date line = "author " + " " * 32000 t = time.perf_counter() parse_actor_and_date(line) print(f"{time.perf_counter() - t:.1f} s") # ~7 s on 3.1.62 ``` End to end, reading `Commit.author` on a commit object whose author line is 32 KB of spaces followed by any malformed tail (no epoch, no timezone, non-numeric epoch, or bad timezone) takes about 7.2 s per access. The same line with a valid `1191999972 -0700` tail takes 1.3 ms. Note for reproducing: `git hash-object -t commit -w` refuses such an object (`missingEmail`), so the test object was written with `--literally`. A remote can still serve one, because `fetch.fsckObjects` and `transfer.fsckObjects` default to false. This is the same delivery model as CVE-2026-87819 ("craft a commit object with a malformed author field"). ### Impact CPU exhaustion (CWE-1333) in any application that uses GitPython to read commits or tags from an untrusted repository: CI tooling, repository scanners, code-hosting integrations. Each access to `Commit.author`, `Commit.committer` or `TagObject.tagger` on a crafted object costs seconds, and a repository can contain many such objects. ### Suggested fix Match the leading field name with `.[^ ]*` instead of `.+?`. Both stop at the first space after the first character, but `.[^ ]*` cannot overlap the following `(.*)`, so the backtracking disappears: ```python _re_actor_epoch = re.compile(r"^.[^ ]* (.*) (\d+) ([+-]\d+).*$") _re_only_actor = re.compile(r"^.[^ ]* (.*)$") ``` Verification on current main (73a72c9): - Differential over 368 actor lines (real-world shapes plus edge cases): 0 differences in match groups for either regex. - `' '*n` at 64 KB: 32,170 ms before, 0.98 ms after. Valid lines are unchanged (0.01 ms). - The existing test suite gives the same pass/fail set with and without the change - The new regression test, `test_parse_actor_and_date_malformed_line_is_linear`, fails on the old pattern (8.4 s) and passes with the fix (0.5 s). The full patch (fix plus regression test) is attached below. ```diff diff --git a/git/objects/util.py b/git/objects/util.py index a68d701f..d8c0594d 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -319,8 +319,11 @@ def parse_date(string_date: Union[str, datetime]) -> Tuple[int, int]: # Precompiled regexes -_re_actor_epoch = re.compile(r"^.+? (.*) (\d+) ([+-]\d+).*$") -_re_only_actor = re.compile(r"^.+? (.*)$") +# The leading field name ("author", "committer", "tagger") is matched with ``.[^ ]*`` rather than +# ``.+?``: both stop at the first space after the first character, but ``.[^ ]*`` cannot overlap the +# following ``(.*)``, so a line with no valid trailing epoch/offset no longer backtracks quadratically. +_re_actor_epoch = re.compile(r"^.[^ ]* (.*) (\d+) ([+-]\d+).*$") +_re_only_actor = re.compile(r"^.[^ ]* (.*)$") def parse_actor_and_date(line: str) -> Tuple[Actor, int, int]: diff --git a/test/test_util.py b/test/test_util.py index cf4299d5..e1631420 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -22,6 +22,7 @@ from git.cmd import dashify from git.objects.util import ( altz_to_utctz_str, from_timestamp, + parse_actor_and_date, parse_date, tzoffset, utctz_to_altz, @@ -571,6 +572,30 @@ class TestUtils(TestBase): Actor("name last another", "some-very-long-email@example.com"), ) + def test_parse_actor_and_date_malformed_line_is_linear(self): + # A long author line whose trailing epoch/offset is missing or malformed used to make + # _re_actor_epoch backtrack | GCVE Database | ||
| High | 7.1 | EPSS 0.3% | High7.1EPSS 0.3%GCVE DatabaseCVE-2026-110855 ### Summary `Repo._clone()`'s handling of `separate_git_dir` never disables `Git.polish_url()`'s environment-variable expansion, unlike every other caller of that helper that handles attacker-influenced data. Since `separate_git_dir` can be derived from an attacker-controlled `.gitmodules` submodule name, a completely ordinary `clone` + `submodule_update(init=True, recursive=True)` on a malicious repository expands an environment variable of the attacker's choosing and exposes its value both as a persistent on-disk directory name and, whenever DEBUG-level logging is enabled (a common CI/verbose-logging configuration), in a `Popen(...)` log line that is not redacted the way credential-bearing arguments are. ### Root cause / Details `git/repo/base.py`'s `_clone()` has: ```python sep_dir = kwargs.get("separate_git_dir") if sep_dir: kwargs["separate_git_dir"] = Git.polish_url(sep_dir) ``` This is the only remaining `Git.polish_url()` call site handling attacker-influenced data that does not pass `expand_vars=False` (there is one other unguarded call site, `clone_path` at line 1537, but its input is the caller's own local destination path, not attacker-controlled). The sibling fixes for `GHSA-94p4-4cq8-9g67`/`GHSA-rwj8-pgh3-r573` added `expand_vars=False` to `clone_url` (5 lines below this one, in the same function), `git/repo/base.py:1599`, `remote.py:814`, and `submodule/base.py:686` — this one did not receive it. (`submodule/root.py` also currently sets `expand_vars=False` at a related call site, but that was added incidentally by a later, unrelated commit, not as part of these security fixes.) `Git.polish_url()`'s docstring itself says: "Disable this for values obtained from an untrusted source, such as remote URLs." Its non-Cygwin branch does plain `os.path.expandvars()` on the entire string, including embedded variables anywhere in an absolute path. `separate_git_dir` reaches `_clone()` from `Submodule._clone_repo()` (`git/objects/submodule/base.py`) as `module_abspath = osp.join(parent_repo.git_dir, "modules", name)`, where `name` is the raw `.gitmodules` submodule section name — fully attacker-controlled content of a cloned repository. `_validated_name()` (added by the fix for `GHSA-hmq2-w58f-27jc`) only rejects names that are absolute, contain a drive letter, or contain `..` path components; it does not reject `$`. `_need_gitfile_submodules()` is true for git ≥ 1.7.5, i.e. the default path for virtually every real-world installation. ### Reproduction (live, against real, unmodified GitPython code, no monkeypatching) Built a hostile repository with a plain, ordinary git command: `git submodule add --name '$GPY_SECRET_MARKER' ../evil_submodule sub`. Set an environment variable in the "victim" process (standing in for a real CI secret such as `AWS_SECRET_ACCESS_KEY`): `GPY_SECRET_MARKER=AKIA_TOTALLY_FAKE_BUT_SECRET_LOOKING_VALUE_12345`. Then ran the exact ordinary workflow the `GHSA-hmq2-w58f-27jc` advisory's own PoC also uses: ```python repo = Repo.clone_from(evil_parent_url, victim_clone_dir) repo.submodule_update(init=True, recursive=True) ``` Observed result (`ls .git/modules/`): ``` .git/modules/AKIA_TOTALLY_FAKE_BUT_SECRET_LOOKING_VALUE_12345/ (a full git repo: HEAD, config, objects/, refs/, hooks/) ``` The literal `$GPY_SECRET_MARKER`-named directory was never created — only the expanded one exists, proving expansion happened before the value reached the `git clone --separate-git-dir=...` subprocess argument. The persistent, expanded-name directory under `.git/modules/` is itself the primary disclosure channel — it survives on disk for as long as the clone does. A second, independent channel: with the `git.cmd`/`git.repo.base` loggers at DEBUG level (a common CI/verbose-logging setting, not requiring any special flag), GitPython's own `Popen(...)` debug log line prints the fully expanded `--separate-git-dir=<value>` subprocess argument verbatim — `remove_password_if_present()` only redacts URL passwords/Authorization headers, not path-style arguments like this one, so the expanded value is not scrubbed. (The call also fails downstream with an `OSError`/`FileNotFoundError` referencing the original, unexpanded name, since `_write_git_file_and_module_config()` looks for a directory using the pre-expansion name — but that error message itself does not contain the expanded/secret value.) **Control**: the same call with an ordinary (no `$`) submodule name succeeds normally and creates `.git/modules/<name>` as expected — isolating the gap to submodule names containing environment-variable syntax specifically. ### Already-known check Checked all 35 published `gitpython-developers/GitPython` security advisories in full text. `GHSA-hmq2-w58f-27jc` covers the same `name` → `.git/modules/<name>` path but for path-traversal (`../../..`), not variable expansion. `GHSA-59cr-6r3x-644w` covers the sibling `path` field reaching unguarded `os.makedirs()`. `GHSA-94p4-4cq8-9g67`/`GHSA-rwj8-pgh3-r573` co | GCVE Database | ||
| Medium | 6.5 | EPSS 0.3% | CVE-2026-116686semgrep · Model Context Protocol Python SDK: Client fetched server-chosen $ref URLs while validating tool results against output Medium6.5EPSS 0.3%GCVE DatabaseCVE-2026-116686 ### Summary In affected versions, when a connected MCP server declared an `outputSchema` for a tool, the SDK's client validated each `call_tool` result with `jsonschema`'s default resolver, which fetches any `$ref` it cannot resolve locally by opening the URL. A server could therefore put an `http://`, `https://` or `file://` reference in a tool's output schema and make the client: - request that URL, or read that local file, from the client's own network position on every call of the tool (the content was used only as a schema and never sent back to the server), and - stall: the fetch ran synchronously on the event loop with no timeout, so a target that accepted the connection and then went quiet froze every session in the process for as long as it held the connection. ### Am I affected? Yes, if your application uses the SDK as an MCP client (`ClientSession` over any transport, `ClientSessionGroup`, or the 2.x `Client`) and calls tools on servers you do not fully control. There was no setting that turned the validation off. Affected versions: 1.10.0 through 1.29.1, and 2.0.0 through 2.1.1. Not affected: applications that only build servers. If nothing in your code uses `ClientSession`, `ClientSessionGroup` or the 2.x `Client` class, you are not affected. ### Fix Upgrade to `mcp` 1.30.0 (1.x) or 2.2.0 (2.x) or later. The client now resolves references only within the schema document the server sent (plus the standard JSON Schema metaschemas), so result validation performs no network or file access. A tool whose output schema points outside its own document fails at call time with `RuntimeError` (`Invalid schema for tool ...`); in-document references such as `#/$defs/...` work as before. Until you can upgrade, call tools only on servers you trust. As a partial stopgap, skip tools whose `outputSchema` contains an `$id`, or a `$ref` / `$dynamicRef` that does not start with `#`. | GCVE Database | ||
| Medium | 5.9 | EPSS 0.4% | CVE-2026-116685semgrep · Model Context Protocol Python SDK: HTTP client transports followed cross-origin redirects carrying custom headers and Medium5.9EPSS 0.4%GCVE DatabaseCVE-2026-116685 ### Summary In affected versions, the SDK's Streamable HTTP and SSE client transports followed HTTP redirects to any host. If the configured MCP endpoint answered with a redirect to a different origin: - custom headers set on the client (for example an `X-API-Key` credential) were sent to that origin, and - on a `307` or `308` redirect the request body was re-sent there too. The SDK's OAuth client providers followed redirects on their own discovery, registration and token requests in the same way. A lone `Authorization` bearer header was not exposed; the HTTP library already drops it on cross-origin redirects. Anyone able to make an endpoint you trust redirect elsewhere (a stale redirect to a lapsed domain, a misrouted proxy rule, redirect logic an attacker can influence) could capture that material. A server that is itself malicious gains nothing this way; it already receives your requests directly. ### Am I affected? Yes, if your code uses the SDK as an MCP client over HTTP through any of: - `streamable_http_client` (on 2.x also `Client` given an `http://` or `https://` URL) - `streamablehttp_client` or `sse_client` - `ClientSessionGroup` with HTTP server parameters - the internal `create_mcp_http_client` helper This includes an HTTP client you passed in yourself with `follow_redirects=True`. Affected versions: 1.8.0 through 1.29.1, and 2.0.0 through 2.1.1. Not affected: - MCP servers - stdio clients - a client you supplied with `follow_redirects` left at its default of `False` ### Fix Upgrade to `mcp` 1.30.0 (1.x) or 2.2.0 (2.x) or later; no code changes are needed. The transports, and the OAuth providers' own requests, now follow a redirect only when it stays on the endpoint's origin (same scheme, host and port, or an `http` to `https` upgrade on the same host) and keeps the request method. Any other redirect is not followed and the request fails with an error naming the target (`MCPError` on 2.x, `httpx.HTTPStatusError` on 1.x), whichever HTTP client the transport uses. The internal `create_mcp_http_client` helper no longer turns redirect following on, so code calling it directly now receives the redirect response instead. If your client currently reaches its server through a redirect to another host, including a server that needs no credentials, it will stop connecting after the upgrade: set the endpoint URL to the redirect's target. Until you can upgrade, supply your own HTTP client with `follow_redirects` left at `False` (`http_client=` on `streamable_http_client`; `httpx_client_factory=` on `sse_client` and `streamablehttp_client`; `ClientSessionGroup` has no such parameter). If a client may already have followed a redirect to a host you do not control, rotate the credentials it carried. <details> <summary>If you genuinely need the old behaviour (unsupported)</summary> This restores what this advisory fixes: redirects to any host are followed again, carrying your custom headers and, on `307`/`308`, the request body, OAuth token requests included. Prefer setting the endpoint URL to the redirect's target. If you cannot, and you accept forwarding credentials to the redirect target, give the transport an HTTP client that follows redirects regardless of what the SDK asks for: ```python import httpx2 # on 1.x: import httpx and subclass httpx.AsyncClient instead class FollowRedirectsAnyway(httpx2.AsyncClient): """Follows redirects to ANY origin. Deliberately gives up the SDK's same-origin protection.""" async def send(self, request, *, follow_redirects=False, **kwargs): return await super().send(request, follow_redirects=True, **kwargs) http_client = FollowRedirectsAnyway(timeout=httpx2.Timeout(30, read=300)) # streamable_http_client(url, http_client=http_client) # sse_client / streamablehttp_client take a factory instead: def client_factory(headers=None, timeout=None, auth=None): return FollowRedirectsAnyway(headers=headers, auth=auth, timeout=timeout if timeout is not None else httpx2.Timeout(30, read=300)) # sse_client(url, httpx_client_factory=client_factory) ``` The redirect handling is then the HTTP library's own, as before the fix: a lone `Authorization` header is still dropped on cross-origin hops and `301`/`302`/`303` become `GET`. It is not something the SDK supports or tests. </details> --- | GCVE Database | ||
| Medium | 4.3 | EPSS 0.4% | Medium4.3EPSS 0.4%GCVE Database ### Summary The LangGraph Python SDK v3 streaming client interpolates caller-supplied `thread_id` and `assistant_id` values into request paths without encoding them at four synchronous and asynchronous call sites. Relative path segments can therefore redirect an authenticated GET request to another path on the same configured server. ### Details The affected `get_tree` and `_fetch_state` implementations build request paths from raw identifiers rather than passing each identifier through the SDK's existing `_quote_path_param` helper. HTTP path normalization can collapse values containing relative segments before dispatch while retaining the client's authentication headers. `langgraph-sdk` versions 0.4.0 through 0.4.5 are affected. The v3 stream module is not present in the 0.3.x release line. `RemoteGraph` v3 streaming in `langgraph` 1.2.3 and later is also affected when used with a vulnerable `langgraph-sdk` version. It passes `config["configurable"]["thread_id"]` into `threads.stream(...)`, and its dependency range permits affected SDK versions. ### Impact An application is affected only if it passes attacker-controlled text as a streaming `thread_id` or `assistant_id`. In that configuration, an attacker may select another readable path on the configured server and cause the SDK to return that response to the application. Through `thread_id`, the attacker can also control the query string, for example by targeting `/store/items?namespace=...&key=...`; `assistant_id` controls only the path. The affected requests are GET operations, so this issue does not provide a write primitive. The LangGraph Platform is not exposed to this issue. ### Remediation Upgrade `langgraph-sdk` to version 0.4.6 or later. The fix encodes `assistant_id` in `get_tree` and `thread_id` in `_fetch_state` with `_quote_path_param` in both synchronous and asynchronous stream modules. If you use `RemoteGraph`, also upgrade `langgraph` to version 1.2.14 or later, which requires `langgraph-sdk>=0.4.6`. | GCVE Database | ||
| High | 8.7 | High8.7CVE Database V5CVE-2026-92744 CVE-2026-92744 is a high-severity vulnerability in Delinea Privilege Manager where the agent lacks proper authentication for critical functionality. This flaw allows a remote unauthenticated attacker to execute arbitrary code with LocalSystem privileges under certain conditions. The affected versions include 10.1.0 through 12.0.5796 and 13.0.0 through 13.0.1058. No public exploits or patches are currently documented. | CVE Database V5 | |||
| High | 8.7 | CVE-2026-107238Veeam Management Pack for Microsoft System Center · Deserialization of Untrusted Data High8.7CVE Database V5CVE-2026-107238 CVE-2026-107238 is a high-severity vulnerability in Veeam Management Pack for Microsoft System Center versions 9.0 up to but not including 13.1.0.334. It allows an authenticated domain user to execute remote code with LocalSystem privileges on servers running the Collector service. The flaw is due to deserialization of untrusted data (CWE-502). A fix is available in version 13.1.0.334. | CVE Database V5 | |||
| High | 8.8 | High8.8CVE Database V5CVE-2026-107239 CVE-2026-107239 is a high-severity vulnerability in Veeam Management Pack for Microsoft System Center that allows a member of the Veeam Virtualization Extensions Users group to perform remote code execution on servers running the Collector service. The flaw is a relative path traversal (CWE-23) issue affecting versions from 9.0 up to but not including 13.1.0.334. A fix is available in version 13.1.0.334. | CVE Database V5 | |||
| Low | 3.3 | EPSS 0.1% | CVE-2026-112941shodan · filelock: SoftReadWriteLock follows a lock tree swapped in after its first acquire and writes lock records into Low3.3EPSS 0.1%GCVE DatabaseCVE-2026-112941 `SoftReadWriteLock` (and `AsyncSoftReadWriteLock`, which wraps one) checks once per lock object, by name, that `<path>.rw`, `<path>.rw/gen` and `<path>.rw/holders` are real directories and not symbolic links (files that redirect to another path). It then keeps only a boolean. Every later record operation resolves the full path again, and `O_NOFOLLOW` (a flag that makes an open fail when the final name is a symbolic link) protects the last component only. Another local user (the other user below) who can write the directory holding the lock, or who created `<path>.rw` first in a sticky directory such as `/tmp` (the sticky bit is a directory flag that lets only a file's owner rename or remove it), can put a tree under that name whose `gen` or `holders` is a symbolic link into a directory of the user running the lock (the lock user below). A long-running process of the lock user that has already passed the check then creates, renames over and deletes files there with the lock user's privileges, including in `0700` directories the other user cannot open. The names and bytes are filelock's own lock records, not chosen by the other user. The scanner's reproducer replaces `.git/HEAD` in the lock user's private repository so that `git status` fails. There is no race. Affected versions: filelock >= 4.1.0 and <= 4.2.0. Anthropic's OSS Scanner reported it in private as `ANT-2026-WXXRI4M4` [high, CWE-367], titled "SoftReadWriteLock follows gen dir swapped for a symlink after first acquire". ## Severity **CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N, 3.3 (Low).** `AV:L` (local): the other user needs an account on the same host. `AC:L` (low attack complexity): the swap happens between two acquisitions, with no race to win. `PR:L` (low privileges): a local user with ordinary privileges is enough. `UI:N` (no user interaction): the lock user's process acts on its own. `C:N` (no confidentiality impact): the other user learns nothing. `I:L` (low integrity impact): the lock user's process replaces a file named `HEAD`, creates `epoch` and generation records, and deletes files whose names match the protocol's patterns, in any directory the lock user can write, with the lock user's privileges. The other user does not choose the bytes, but the files are gone and the chosen directory (a git repository) stops working. `A:N` (no availability impact): the lock itself keeps working and the lock user's process does not stall. The scanner rated it high; we scored the vector for a lock user with ordinary privileges. Classification from the external review: conditional security. It is a cross-user replacement of a cached `.rw` tree that redirects later writes of the lock user through an intermediate symbolic link. It needs a planted tree in sticky `/tmp` or a writable parent without the sticky bit. Scoring note: we rate integrity low rather than high. The bytes written through the swapped tree are filelock's own records, not content the other user chose, so the lower reading of the CVSS integrity metric applies. ## Scope and preconditions - Trust boundary: the other party is a different local account with write access to the lock's parent directory. Without the sticky bit (a `0777` scratch or spool directory, or a group-writable project or cache directory shared with that account's group) write access alone is enough. The other user renames the lock user's own `0700` `<path>.rw` aside and puts its tree in place. In a sticky parent such as `/tmp` the extra precondition is that it created `<path>.rw` (with real `gen` and `holders` directories, mode `0777`) before the lock user's first acquire. `prepare()` accepts an existing tree whoever owns it, and the other user later replaces its own `gen` with a symbolic link. `fs.protected_symlinks=1` (a Linux setting that refuses to follow another user's symbolic link in a world-writable sticky directory) does not intervene, because the link sits inside the other user's own non-sticky `<path>.rw`, not in the sticky parent (the scanner ran with that setting). - Lock user: any process that keeps one `SoftReadWriteLock` or `AsyncSoftReadWriteLock` object (a singleton per path by default) and acquires it more than once, or is waiting for it when the swap happens. A fresh process that meets the planted tree gets `RuntimeError`. Between two acquisitions there is no timing to win. - Targets: any directory the lock user can write, on any filesystem. The other user needs only to know its path (`/home/victim/project/.git` in the reproducer, under a `0700` home directory the other user cannot open). - Platform: observed on Linux. The code path is the same on every POSIX platform and on Windows, where a junction (a Windows directory that points at another directory) at a directory component plays the symbolic link's part. The scanner ran Linux only. - Ancestor boundary: protection against another local user is possible only while no other user can replace the lock's directory or any of its ancestors. That | GCVE Database | ||
| High | 7.1 | EPSS 0.2% | CVE-2026-112940shodan · filelock ReadWriteLock: SQLite reopens the lock database by name, so a planted symlink or journal file makes the lock High7.1EPSS 0.2%GCVE DatabaseCVE-2026-112940 `ReadWriteLock` and `AsyncReadWriteLock` (the SQLite-backed reader/writer lock) hand SQLite a name they do not control. On Linux the lock validates its database through a descriptor (the number the kernel gives a process for an open file). It then connects SQLite to `/proc/self/fd/<n>`, a Linux path that names the process's own open file `n`. SQLite turns that path back into a plain path and reopens it by name, following symlinks (a symlink is a file that points at another path). On every POSIX route SQLite also acts on `-journal`, `-wal` and `-shm` files it finds beside that name. Those are SQLite's side files: the rollback journal (pages saved to undo an interrupted write), the write-ahead log or WAL (changes kept outside the main file), and the shared-memory index for the WAL. Another local user who can create or rename entries in the directory that holds the lock database can turn this against the user running the lock (below: the lock user). Such a directory is a `0777` or group-writable cache, build or project directory without the sticky bit (a directory flag that lets only a file's owner remove or rename it). That other user can make the lock user's ordinary `read_lock()` or `write_lock()` write bytes of the other user's choosing into the lock user's own files. The same position lets the other user make that call create or unlink files of the lock user, admit a reader beside a writer, or kill the holder. All of this runs with the lock user's privileges. In a sticky directory such as `/tmp` a planted `-wal` file makes every acquire fail. Affected versions are filelock 3.20.4 through 4.2.0 (the `/proc/self/fd` redirect since 4.0.9, the side-file variants since 3.20.4). Anthropic's OSS Scanner reported this in private as `ANT-2026-KMJ2575J` [high, CWE-363]: ReadWriteLock: SQLite still follows planted symlinks, overwriting files. ## Severity **CVSS 3.1 `CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H` scores 7.1 (High).** `AV:L` (local): the other user needs an account on the same host. `PR:L` (low privileges): an ordinary account is enough. `UI:N` (no user interaction). `AC:L` (low attack complexity): the planted-journal variant needs no race, and the redirect won 77 of 77 attempts. `I:H` (high integrity impact): the other user writes bytes of their choosing into files of the lock user and unlinks files of their choosing. `A:H` (high availability impact): the other user makes every acquire fail or kills the lock holder. `C:N` (no confidentiality impact): the other user learns nothing. `S:U` (scope unchanged): the effect stays within the lock user's own privileges. Classification from the external review: **conditional security**, a cross-user effect that exists only when another user can change names in the lock database's directory. The review notes that the 77/77 inotify result (inotify is a Linux interface that reports file events) is stronger evidence than a theoretical TOCTOU (a time-of-check to time-of-use race). The scanner rated it high. The review accepts the mechanism and narrows it to that precondition. ## Scope and preconditions - The other party is a different local account that can create or rename entries in the directory that holds the lock database. That is a `0777` directory or a group-writable one (including a setgid `2775` project directory shared with that account), in each case without the sticky bit. The lock user's lock must be able to open the database read-write, so a database the other user creates is `0666`. filelock itself creates the database `0600`. - Sticky directories (`/tmp`, `/var/tmp`, any `1777` directory) narrow the finding rather than exclude it. The redirect and the journal replay do not reach a database the lock user owns there (tested with `fs.protected_regular=2`). What remains is a planted non-empty `<db>-wal` (any mode; a directory or FIFO works too) that the lock user cannot unlink. It makes every acquire raise `sqlite3.OperationalError: disk I/O error`. A hard link (a second name for the same file) to a file of the lock user at the lock path also remains, if `fs.protected_hardlinks=0`. - Two kernel settings matter. `fs.protected_regular` (1 or 2 is the systemd default on most distributions; the kernel's own default is 0) keeps a user from opening a file another user owns in a sticky world-writable directory. Where it is 0, the other user can own the database in `/tmp`, and SQLite follows a planted symlink itself (not tested). `fs.protected_hardlinks=1` (most distributions; kernel default 0) blocks a hard link of a lock user's file onto the lock path. Neither setting affects a non-sticky shared directory. - Platform and route. The `/proc/self/fd` redirect is Linux with `/proc` mounted, 4.0.9 and later. The side-file variants (`-journal`, `-wal`, `-shm`) exist on every POSIX route. On the hard-link route (macOS since 4.1.1 for every connection, Linux without `/proc`) they need the link-directory swap described under "Mechanism", so a non-sticky parent. N | GCVE Database | ||
| Medium | 4.7 | EPSS 0.1% | CVE-2026-112939shodan · filelock: SoftFileLock removes a live holder's marker when another user forges the PID in a writable marker Medium4.7EPSS 0.1%GCVE DatabaseCVE-2026-112939 `SoftFileLock`, `MarkerSoftFileLock`, `SoftFileLease`, their async variants and a `FileLock` that has fallen back to a soft lock on `ENOSYS` decide from the record inside the marker file whether its owner is gone. The marker is the file whose presence says a process holds the lock. They do not check who could have written that record. A local user who can write the marker but cannot remove it rewrites the holder's record to name a dead PID (process id). That position arises with an explicit `mode` with group or other write bits, or the default mode under a lax umask (the process setting that strips permission bits from new files), in a sticky or otherwise non-removable directory. The sticky bit is a directory flag that lets only a file's owner remove or rename it. The next contender running as the holder's account, or root, judges the lock stale, removes the live marker on that user's behalf and acquires it. Two processes then run the critical section together and neither gets an error. Affected versions are filelock 3.22.0 through 4.2.0 (finding `ANT-2026-W1H9NXUH`). ## Severity **CVSS 3.1 `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N` scores 4.7 (Medium).** `AV:L` (local): the other user needs an account on the same host. `PR:L` (low privileges): an ordinary account is enough. `UI:N` (no user interaction). `AC:H` (high attack complexity): the marker must carry group or other write bits (an explicit permissive `mode`, or umask `002`/`000` with a shared group), and a contender of the holder's account must ask for the lock while the forged record is in place. `I:H` (high integrity impact): two holders then write whatever the lock protects, and the holder is not told. `C:N` and `A:N` (no confidentiality or availability impact). `S:U` (scope unchanged). Classification from the review: **security, conditional** on another user being able to write the live marker. The scanner rated it high (CWE-345). The default mode under umask `022` is not affected. ## Scope and preconditions - The other user can write the marker. The application passes a `mode` with group or other write bits, which `fchmod` applies as given since 4.1.0. `0o666` is a common choice for a lock shared between accounts, although a soft-lock contender of another account waits as well on a `0o644` marker, since contenders only read and rename. Or the application uses the default mode (`0o666` through the umask) under umask `002` in a directory whose group the other user belongs to, or under umask `000`. The default mode under umask `022` is not affected. - The other user cannot remove the marker itself: the directory is sticky, or the other user cannot write it at all (a `0755` directory owned by the lock's user gives the same overlap). In a non-sticky shared directory the other user can unlink the marker without filelock, and filelock adds nothing. - A contender that may rename the marker asks for the lock: another process of the holder's account (several workers of one user) or root. A contender of another unprivileged account cannot rename the marker in a sticky directory and times out. - The finding reaches every class that publishes a soft marker: `SoftFileLock`, `MarkerSoftFileLock`, `SoftFileLease`, their async variants, and `FileLock(mode=0o666)` on a filesystem where `flock` (a kernel lock on an open file) fails with `ENOSYS`. These protections against another local user hold only when no other user can replace the lock's directory or any of its ancestors. That holds when each ancestor belongs to the user or root and is either not writable by others or sticky. A user who can rename entries in a non-sticky directory above the lock controls every name below it, which no file-based lock can defend against. ## Affected code At the scanned commit b831496: the create mode in [`SoftFileLock._acquire`](https://github.com/tox-dev/filelock/blob/b831496b0273ecd11bf02b8d080ca8b673a538c1/src/filelock/_soft.py#L67-L71), the explicit-mode `fchmod` in [`_apply_explicit_mode`](https://github.com/tox-dev/filelock/blob/b831496b0273ecd11bf02b8d080ca8b673a538c1/src/filelock/_soft.py#L100-L105), the trusting verdict in [`_try_break_stale_lock`](https://github.com/tox-dev/filelock/blob/b831496b0273ecd11bf02b8d080ca8b673a538c1/src/filelock/_soft.py#L107-L123) and the [`owner_is_stale`](https://github.com/tox-dev/filelock/blob/b831496b0273ecd11bf02b8d080ca8b673a538c1/src/filelock/_identity.py#L64-L79) it relies on; the same call in [`MarkerSoftFileLock`](https://github.com/tox-dev/filelock/blob/b831496b0273ecd11bf02b8d080ca8b673a538c1/src/filelock/_marker.py#L74-L81) and [`SoftFileLease`](https://github.com/tox-dev/filelock/blob/b831496b0273ecd11bf02b8d080ca8b673a538c1/src/filelock/_lease.py#L229-L256). ## Introduced The stale verdict that trusts the record came in with e35c3afac473 ("feat(soft): detect and break stale locks", #476, 2026-02-13), first released in 3.22.0. From then on a marker left group- or other-writable by the umask is forgeable. The re | GCVE Database | ||
| Medium | 5.8 | CVE-2026-112938shodan · filelock: StrictSoftFileLock follows a symbolic link swapped in at <path>.filelock and creates claim files in a Medium5.8GCVE DatabaseCVE-2026-112938 A vulnerability in filelock versions >=1.31.0_11 and <1.31.0_12 allows a local user with write access to a shared directory to swap a symbolic link in place of a lock directory between a check and subsequent operations. This can cause the lock user to create claim files in an attacker-chosen directory, potentially disrupting other locks by causing stranded claims that lead to denial of service until manually cleared. The issue requires a race condition and specific directory permissions, and has medium severity with a CVSS score of 5.8. | GCVE Database | |||
| Medium | 6.1 | EPSS 0.2% | CVE-2026-112937shodan · filelock: ReadWriteLock on the hard-link route deletes any directory another user renames onto its .filelock-* link Medium6.1EPSS 0.2%GCVE DatabaseCVE-2026-112937 The filelock library versions 4.1.0 through 4.2.0 contain a vulnerability in the ReadWriteLock and AsyncReadWriteLock mechanisms where private .filelock-* directories are deleted recursively without verifying the directory's identity. This can be exploited by another local user who can rename directories in the shared parent directory (without sticky bit protection) to cause the lock user's process to delete arbitrary directories with the lock user's permissions. The issue affects macOS since version 4.1.1 and POSIX systems without a usable /proc/self/fd, such as chroot or containers without /proc mounted. The vulnerability has a CVSS 3.1 score of 6.1 (Medium) and results in high integrity impact due to unauthorized deletion of directory trees. | GCVE Database | ||
| Medium | 6.1 | EPSS 0.2% | Medium6.1EPSS 0.2%GCVE DatabaseCVE-2026-112936 The ReadWriteLock and AsyncReadWriteLock classes in the affected software resolve lock file paths by following symlinks, which allows a local attacker with write access to the lock directory to manipulate the locking mechanism. This can cause the victim process to create or write files at unintended locations, break mutual exclusion between processes, or cause database locking conflicts. The issue affects versions from 1.31.0_9 up to but not including 1.31.0_11 and has been fixed in version 1.31.0_11. | GCVE Database | ||
| Medium | 4.7 | EPSS 0.1% | CVE-2026-112935shodan · filelock: SoftFileLock stale-lock break removes a live lock file on a reused inode number and admits two holders Medium4.7EPSS 0.1%GCVE DatabaseCVE-2026-112935 A vulnerability in the filelock library versions 3.22.0 through 4.2.0 allows a local user with write access to a sticky shared directory (e.g., /tmp) on ext4 or xfs filesystems to cause two processes of the lock owner to simultaneously hold the same lock. This occurs due to inode reuse and a race condition in the stale-lock break logic of SoftFileLock and related classes, leading to integrity violations without confidentiality or availability impact. | GCVE Database | ||
| Medium | 6.1 | EPSS 0.2% | CVE-2026-112934shodan · filelock: SoftReadWriteLock holder refresh writes through a hard link and hangs on a named pipe that another local Medium6.1EPSS 0.2%GCVE DatabaseCVE-2026-112934 CVE-2026-112934 describes a vulnerability in filelock versions >=1.31.0_11 and <1.31.0_12 where the SoftReadWriteLock holder refresh mechanism can be manipulated by a local user who owns the lock's .rw directory tree. This user can cause the lock user's process to hang indefinitely by placing a FIFO (named pipe) in the holders directory, blocking write operations and preventing heartbeat refreshes. Additionally, on systems that permit hard links to files not owned by the user (e.g., Linux with fs.protected_hardlinks=0), the attacker can cause the lock user to overwrite part of one of its own private files. The vulnerability has no confidentiality impact but can cause integrity degradation and high availability impact by allowing two processes to believe they hold the write lock simultaneously. A fix is available in version 1.31.0_12. | GCVE Database | ||
| Medium | 5.5 | EPSS 0.2% | CVE-2026-112915shodan · filelock ReadWriteLock: a crafted lock database makes every acquire stall or hang regardless of timeout Medium5.5EPSS 0.2%GCVE DatabaseCVE-2026-112915 A vulnerability in the filelock library's ReadWriteLock and AsyncReadWriteLock components allows a local user with write access to the lock database file path to cause every acquire operation to stall or hang indefinitely, ignoring timeout and blocking settings. This is achieved by placing a crafted SQLite database file at the lock path, which causes the lock acquisition to consume excessive CPU or memory resources, resulting in denial of service. The issue affects filelock versions 3.20.4 through 4.2.0 and has no impact on confidentiality or integrity, only availability. | GCVE Database | ||
| Medium | 5.3 | EPSS 0.2% | CVE-2026-112914shodan · filelock: SoftFileLock stale-break restore follows a swapped-in symbolic link and hard-links a file of the user Medium5.3EPSS 0.2%GCVE DatabaseCVE-2026-112914 A vulnerability in filelock versions 4.1.0 through 4.2.0 allows a local unprivileged user who can write to a shared lock directory without the sticky bit to exploit the soft-lock stale break restore mechanism. This flaw enables the attacker to cause the lock process to create a hard link to one of the lock user's own files, exposing file metadata and allowing the attacker to rename the link. On version 4.2.0, this can lead to truncation of the targeted file by the lock user. The issue arises due to symbolic link following behavior in os.link on CPython 3.12+ on Linux and macOS. The vulnerability requires specific conditions including a writable non-sticky directory shared by users on the same filesystem. The CVSS 3.1 score is 5.3 (Medium). | GCVE Database |
Page 29 of 9,555