CVE-2026-42449
Description
n8n-MCP is an MCP server that provides AI assistants access to n8n node documentation, properties, and operations. In versions 2.47.4 through 2.47.13, the SDK embedder path (N8NDocumentationMCPServer constructor, getN8nApiClient(), and validateInstanceContext()), the synchronous URL validator in SSRFProtection.validateUrlSync() had no IPv6 checks. IPv4-mapped IPv6 addresses such as http://[::ffff:169.254.169.254] bypassed the cloud-metadata, localhost, and private-IP range checks. An attacker able to supply an n8nApiUrl value could cause the server to issue HTTP requests to cloud metadata endpoints, RFC1918 private networks, or localhost services. Response bodies are returned to the caller (non-blind SSRF), and the n8nApiKey is forwarded in the x-n8n-api-key header to the attacker-controlled target. Projects with deployments embedding n8n-mcp as an SDK using N8NDocumentationMCPServer or N8NMCPEngine with user-supplied InstanceContext are affected. The first-party HTTP server deployment was not primarily affected — it has a second async validator (validateWebhookUrl) that catches IPv6 addresses. This issue has been fixed in version 2.47.14. If users are unable to upgrade immediately as a workaround they can validate URLs before passing to the SDK, restrict egress at the network layer, and reject user-controlled n8nApiUrl values.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
n8n-mcpnpm | >= 2.47.4, < 2.47.14 | 2.47.14 |
Affected products
1- Range: >= 2.47.4, < 2.47.14
Patches
19639f7578531Merge commit from fork
9 files changed · +226 −17
CHANGELOG.md+8 −0 modified@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.47.14] - 2026-04-21 + +### Security + +- Fix IPv6-mapped SSRF bypass in synchronous URL validation (GHSA-56c3-vfp2-5qqj, CVSS 8.5 High). `SSRFProtection.validateUrlSync` now rejects IPv4-mapped IPv6 (`::ffff:169.254.169.254`, `::ffff:127.0.0.1`, etc.) and private IPv6 addresses, matching the async webhook validator. The sync gate is the sole SSRF check in the SDK embedder path (`validateInstanceContext` → `getN8nApiClient`), so the bypass enabled cloud metadata access and `x-n8n-api-key` leakage for callers of `N8NDocumentationMCPServer` / `N8NMCPEngine` with user-supplied `InstanceContext`. Reported by @manthanghasadiya. + +Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en + ## [2.47.13] - 2026-04-20 ### Security
dist/utils/ssrf-protection.d.ts+1 −0 modified@@ -1,4 +1,5 @@ export declare class SSRFProtection { + private static isPrivateOrMappedIpv6; static validateWebhookUrl(urlString: string): Promise<{ valid: boolean; reason?: string;
dist/utils/ssrf-protection.d.ts.map+1 −1 modified@@ -1 +1 @@ -{"version":3,"file":"ssrf-protection.d.ts","sourceRoot":"","sources":["../../src/utils/ssrf-protection.ts"],"names":[],"mappings":"AAoDA,qBAAa,cAAc;WAoBZ,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAC1D,KAAK,EAAE,OAAO,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAC;IA2HF,MAAM,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAkD/E"} \ No newline at end of file +{"version":3,"file":"ssrf-protection.d.ts","sourceRoot":"","sources":["../../src/utils/ssrf-protection.ts"],"names":[],"mappings":"AAqDA,qBAAa,cAAc;IAgBzB,OAAO,CAAC,MAAM,CAAC,qBAAqB;WAqDvB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAC1D,KAAK,EAAE,OAAO,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAC;IAsHF,MAAM,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CA0D/E"} \ No newline at end of file
dist/utils/ssrf-protection.js+24 −6 modified@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.SSRFProtection = void 0; const url_1 = require("url"); const promises_1 = require("dns/promises"); +const net_1 = require("net"); const logger_1 = require("./logger"); const CLOUD_METADATA = new Set([ '169.254.169.254', @@ -28,6 +29,25 @@ const PRIVATE_IP_RANGES = [ /^0\./, ]; class SSRFProtection { + static isPrivateOrMappedIpv6(hostname) { + if (!(0, net_1.isIPv6)(hostname)) + return false; + if (hostname.startsWith('::')) + return true; + if (hostname.startsWith('0:0:0:0:0:ffff:')) + return true; + if (hostname.startsWith('fe80:')) + return true; + if (/^fe[c-f]/.test(hostname)) + return true; + if (/^f[cd]/.test(hostname)) + return true; + if (hostname.startsWith('2002:')) + return true; + if (hostname.startsWith('64:ff9b:')) + return true; + return false; + } static async validateWebhookUrl(urlString) { try { const url = new url_1.URL(urlString); @@ -94,12 +114,7 @@ class SSRFProtection { : 'Private IP addresses not allowed (use WEBHOOK_SECURITY_MODE=permissive if needed)' }; } - if (resolvedIP === '::1' || - resolvedIP === '::' || - resolvedIP.startsWith('fe80:') || - resolvedIP.startsWith('fc00:') || - resolvedIP.startsWith('fd00:') || - resolvedIP.startsWith('::ffff:')) { + if (SSRFProtection.isPrivateOrMappedIpv6(resolvedIP)) { logger_1.logger.warn('SSRF blocked: IPv6 private address', { hostname, resolvedIP, @@ -152,6 +167,9 @@ class SSRFProtection { : 'Private IP addresses not allowed (use WEBHOOK_SECURITY_MODE=permissive if needed)' }; } + if (SSRFProtection.isPrivateOrMappedIpv6(hostname)) { + return { valid: false, reason: 'IPv6 private/mapped address not allowed' }; + } return { valid: true }; } }
dist/utils/ssrf-protection.js.map+1 −1 modified@@ -1 +1 @@ -{"version":3,"file":"ssrf-protection.js","sourceRoot":"","sources":["../../src/utils/ssrf-protection.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"ssrf-protection.js","sourceRoot":"","sources":["../../src/utils/ssrf-protection.ts"],"names":[],"mappings":";;;AAAA,6BAA0B;AAC1B,2CAAsC;AACtC,6BAA6B;AAC7B,qCAAkC;AAkBlC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAE7B,iBAAiB;IACjB,eAAe;IAEf,0BAA0B;IAC1B,UAAU;IAEV,iBAAiB;IAEjB,aAAa;CACd,CAAC,CAAC;AAGH,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjC,WAAW;IACX,WAAW;IACX,KAAK;IACL,SAAS;IACT,uBAAuB;CACxB,CAAC,CAAC;AAGH,MAAM,iBAAiB,GAAG;IACxB,OAAO;IACP,aAAa;IACb,gCAAgC;IAChC,aAAa;IACb,QAAQ;IACR,MAAM;CACP,CAAC;AAEF,MAAa,cAAc;IAgBjB,MAAM,CAAC,qBAAqB,CAAC,QAAgB;QAGnD,IAAI,CAAC,IAAA,YAAM,EAAC,QAAQ,CAAC;YAAE,OAAO,KAAK,CAAC;QAKpC,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAI3C,IAAI,QAAQ,CAAC,UAAU,CAAC,iBAAiB,CAAC;YAAE,OAAO,IAAI,CAAC;QAGxD,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;QAG9C,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAG3C,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAIzC,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;QAI9C,IAAI,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,OAAO,IAAI,CAAC;QAEjD,OAAO,KAAK,CAAC;IACf,CAAC;IAqBD,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAiB;QAI/C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,SAAG,CAAC,SAAS,CAAC,CAAC;YAC/B,MAAM,IAAI,GAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,QAAQ,CAAiB,CAAC;YAG3F,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,4CAA4C,EAAE,CAAC;YAChF,CAAC;YAGD,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;YAE1C,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvD,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACnC,CAAC;YAGD,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACjC,eAAM,CAAC,IAAI,CAAC,uCAAuC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBACzE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,iCAAiC,EAAE,CAAC;YACrE,CAAC;YAID,IAAI,UAAkB,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAA,iBAAM,EAAC,QAAQ,CAAC,CAAC;gBAC3C,UAAU,GAAG,OAAO,CAAC;gBAErB,eAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9E,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,eAAM,CAAC,IAAI,CAAC,uCAAuC,EAAE;oBACnD,QAAQ;oBACR,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;iBAC9D,CAAC,CAAC;gBACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;YAC3D,CAAC;YAGD,IAAI,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;gBACnC,eAAM,CAAC,IAAI,CAAC,sDAAsD,EAAE;oBAClE,QAAQ;oBACR,UAAU;oBACV,IAAI;iBACL,CAAC,CAAC;gBACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,8CAA8C,EAAE,CAAC;YAClF,CAAC;YAKD,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC1B,eAAM,CAAC,IAAI,CAAC,wEAAwE,EAAE;oBACpF,QAAQ;oBACR,UAAU;iBACX,CAAC,CAAC;gBACH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YACzB,CAAC;YAGD,MAAM,WAAW,GAAG,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAClC,UAAU,KAAK,KAAK;gBACpB,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAGhD,IAAI,IAAI,KAAK,QAAQ,IAAI,WAAW,EAAE,CAAC;gBACrC,eAAM,CAAC,IAAI,CAAC,oDAAoD,EAAE;oBAChE,QAAQ;oBACR,UAAU;iBACX,CAAC,CAAC;gBACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,4CAA4C,EAAE,CAAC;YAChF,CAAC;YAGD,IAAI,IAAI,KAAK,UAAU,IAAI,WAAW,EAAE,CAAC;gBACvC,eAAM,CAAC,IAAI,CAAC,2CAA2C,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;gBACnF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YACzB,CAAC;YAGD,IAAI,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;gBAC5D,eAAM,CAAC,IAAI,CAAC,kCAAkC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChF,OAAO;oBACL,KAAK,EAAE,KAAK;oBACZ,MAAM,EAAE,IAAI,KAAK,QAAQ;wBACvB,CAAC,CAAC,kCAAkC;wBACpC,CAAC,CAAC,mFAAmF;iBACxF,CAAC;YACJ,CAAC;YAGD,IAAI,cAAc,CAAC,qBAAqB,CAAC,UAAU,CAAC,EAAE,CAAC;gBACrD,eAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE;oBAChD,QAAQ;oBACR,UAAU;oBACV,IAAI;iBACL,CAAC,CAAC;gBACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,kCAAkC,EAAE,CAAC;YACtE,CAAC;YAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACzB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QACxD,CAAC;IACH,CAAC;IAaD,MAAM,CAAC,eAAe,CAAC,SAAiB;QACtC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,+BAA+B,EAAE,CAAC;QACnE,CAAC;QAED,IAAI,GAAQ,CAAC;QACb,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,SAAG,CAAC,SAAS,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QACxD,CAAC;QAED,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,4CAA4C,EAAE,CAAC;QAChF,CAAC;QAED,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,EAAE,CAAC;YAC/C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,gCAAgC,EAAE,CAAC;QACpE,CAAC;QAED,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACvD,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACnC,CAAC;QAED,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,iCAAiC,EAAE,CAAC;QACrE,CAAC;QAED,MAAM,IAAI,GAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,QAAQ,CAAiB,CAAC;QAE3F,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;YAC1B,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACzB,CAAC;QAED,IAAI,IAAI,KAAK,QAAQ,IAAI,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,4CAA4C,EAAE,CAAC;QAChF,CAAC;QAED,IAAI,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;YAC1D,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,IAAI,KAAK,QAAQ;oBACvB,CAAC,CAAC,kCAAkC;oBACpC,CAAC,CAAC,mFAAmF;aACxF,CAAC;QACJ,CAAC;QAMD,IAAI,cAAc,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,yCAAyC,EAAE,CAAC;QAC7E,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACzB,CAAC;CACF;AAxPD,wCAwPC"} \ No newline at end of file
package.json+1 −1 modified@@ -1,6 +1,6 @@ { "name": "n8n-mcp", - "version": "2.47.13", + "version": "2.47.14", "description": "Integration between n8n workflow automation and Model Context Protocol (MCP)", "main": "dist/index.js", "types": "dist/index.d.ts",
package-lock.json+2 −2 modified@@ -1,12 +1,12 @@ { "name": "n8n-mcp", - "version": "2.47.10", + "version": "2.47.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "n8n-mcp", - "version": "2.47.10", + "version": "2.47.14", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "1.28.0",
src/utils/ssrf-protection.ts+59 −6 modified@@ -1,5 +1,6 @@ import { URL } from 'url'; import { lookup } from 'dns/promises'; +import { isIPv6 } from 'net'; import { logger } from './logger'; /** @@ -51,6 +52,55 @@ const PRIVATE_IP_RANGES = [ ]; export class SSRFProtection { + /** + * IPv6 addresses that must be blocked: loopback, unspecified, link-local, + * unique-local, site-local (deprecated), IPv4-mapped, IPv4-compatible, + * 6to4, and NAT64-mapped addresses. All of these either represent private + * networks or embed an arbitrary IPv4 address that would bypass IPv4-only + * SSRF checks. + * + * Hostname must be lowercased and bracket-stripped. WHATWG URL parser + * canonicalizes IPv6 literals (zero compression, dotted-quad → hex pairs), + * so prefix matching works against the normalized form. + * + * @security See GHSA-56c3-vfp2-5qqj. The sync validator previously had no + * IPv6 gate, letting `::ffff:169.254.169.254`, `::169.254.169.254`, + * `2002:a9fe:a9fe::`, and `64:ff9b::a9fe:a9fe` reach the HTTP client. + */ + private static isPrivateOrMappedIpv6(hostname: string): boolean { + // Gate on net.isIPv6 so domain names starting with hex-like labels + // (e.g. "fcexample.com") are never misclassified as private IPv6. + if (!isIPv6(hostname)) return false; + + // ::/96 reserved block: unspecified (`::`), loopback (`::1`), IPv4-mapped + // (`::ffff:X`), and deprecated IPv4-compatible (`::X:Y` per RFC 4291) all + // live here. Blocking the whole prefix avoids enumerating subforms. + if (hostname.startsWith('::')) return true; + + // Defensive long-form IPv4-mapped — WHATWG URL normally compresses this, + // but keep the check in case normalization ever changes. + if (hostname.startsWith('0:0:0:0:0:ffff:')) return true; + + // Link-local fe80::/10 + if (hostname.startsWith('fe80:')) return true; + + // Site-local fec0::/10 (deprecated, RFC 3879) — still honored by some stacks. + if (/^fe[c-f]/.test(hostname)) return true; + + // Unique local fc00::/7 (RFC 4193). Covers fc00-fdff in the first hextet. + if (/^f[cd]/.test(hostname)) return true; + + // 6to4 2002::/16 (RFC 3056) — bits 16-47 embed an arbitrary IPv4 address, + // so any 2002: address can tunnel to RFC1918 or metadata endpoints. + if (hostname.startsWith('2002:')) return true; + + // NAT64 64:ff9b::/96 (RFC 6052) and 64:ff9b:1::/48 (RFC 8215) — embedded + // IPv4 in the low 32 bits, same tunneling concern as 6to4. + if (hostname.startsWith('64:ff9b:')) return true; + + return false; + } + /** * Validate webhook URL for SSRF protection with configurable security modes * @@ -165,12 +215,7 @@ export class SSRFProtection { } // Step 7: IPv6 private address check (strict & moderate modes) - if (resolvedIP === '::1' || // Loopback - resolvedIP === '::' || // Unspecified address - resolvedIP.startsWith('fe80:') || // Link-local - resolvedIP.startsWith('fc00:') || // Unique local (fc00::/7) - resolvedIP.startsWith('fd00:') || // Unique local (fd00::/8) - resolvedIP.startsWith('::ffff:')) { // IPv4-mapped IPv6 + if (SSRFProtection.isPrivateOrMappedIpv6(resolvedIP)) { logger.warn('SSRF blocked: IPv6 private address', { hostname, resolvedIP, @@ -244,6 +289,14 @@ export class SSRFProtection { }; } + // SECURITY (GHSA-56c3-vfp2-5qqj): reject IPv4-mapped and private IPv6 + // addresses. Without this, hostnames like `::ffff:169.254.169.254` or + // `::ffff:127.0.0.1` pass the IPv4-only checks above and reach the HTTP + // client. + if (SSRFProtection.isPrivateOrMappedIpv6(hostname)) { + return { valid: false, reason: 'IPv6 private/mapped address not allowed' }; + } + return { valid: true }; } }
tests/unit/utils/ssrf-protection.test.ts+129 −0 modified@@ -562,5 +562,134 @@ describe('SSRFProtection', () => { expect(result.valid).toBe(false); expect(result.reason).toBe('URL fragments are not allowed'); }); + + // GHSA-56c3-vfp2-5qqj — IPv4-mapped IPv6 and private IPv6 addresses + // were skipped by the IPv4-only checks, enabling SSRF to cloud metadata, + // RFC1918 networks, and localhost via SDK embedders. + describe('IPv6 private and IPv4-mapped addresses (GHSA-56c3-vfp2-5qqj)', () => { + it('should reject IPv4-mapped IPv6 cloud metadata and private ranges in strict and moderate modes', () => { + const payloads = [ + 'http://[::ffff:169.254.169.254]', // AWS/Azure IMDS via IPv4-mapped + 'http://[::ffff:127.0.0.1]:5678', // localhost via IPv4-mapped + 'http://[::ffff:10.0.0.1]', // RFC1918 10.x + 'http://[::ffff:192.168.1.1]', // RFC1918 192.168.x + 'http://[::ffff:172.16.0.1]', // RFC1918 172.16.x + ]; + for (const mode of ['strict', 'moderate']) { + process.env.WEBHOOK_SECURITY_MODE = mode; + for (const url of payloads) { + const result = SSRFProtection.validateUrlSync(url); + expect(result.valid, `url=${url} mode=${mode}`).toBe(false); + expect(result.reason).toBe('IPv6 private/mapped address not allowed'); + } + } + }); + + it('should reject long-form IPv4-mapped IPv6 localhost', () => { + delete process.env.WEBHOOK_SECURITY_MODE; + const result = SSRFProtection.validateUrlSync('http://[0:0:0:0:0:ffff:7f00:1]'); + expect(result.valid).toBe(false); + expect(result.reason).toBe('IPv6 private/mapped address not allowed'); + }); + + it('should reject private IPv6 addresses in strict and moderate modes', () => { + const payloads = [ + 'http://[::1]', // IPv6 loopback (strict hits LOCALHOST_PATTERNS first) + 'http://[fe80::1]', // Link-local + 'http://[fc00::1]', // Unique local (literal fc00:) + 'http://[fd00::1]', // Unique local (literal fd00:) + ]; + for (const mode of ['strict', 'moderate']) { + process.env.WEBHOOK_SECURITY_MODE = mode; + for (const url of payloads) { + const result = SSRFProtection.validateUrlSync(url); + expect(result.valid, `url=${url} mode=${mode}`).toBe(false); + } + } + }); + + it('should reject IPv4-compatible IPv6 (::X:Y) that embeds cloud metadata or private IPv4', () => { + // WHATWG URL normalizes ::a.b.c.d into ::XXXX:YYYY hex form. The + // low 32 bits can hold any IPv4 including IMDS/RFC1918/loopback. + const payloads = [ + 'http://[::169.254.169.254]', // → ::a9fe:a9fe (AWS/Azure IMDS) + 'http://[::127.0.0.1]', // → ::7f00:1 (loopback) + 'http://[::10.0.0.1]', // → ::a00:1 (RFC1918) + ]; + for (const url of payloads) { + const result = SSRFProtection.validateUrlSync(url); + expect(result.valid, `url=${url}`).toBe(false); + expect(result.reason).toBe('IPv6 private/mapped address not allowed'); + } + }); + + it('should reject 6to4 (2002::/16) embedding arbitrary IPv4', () => { + const result = SSRFProtection.validateUrlSync('http://[2002:a9fe:a9fe::]'); + expect(result.valid).toBe(false); + expect(result.reason).toBe('IPv6 private/mapped address not allowed'); + }); + + it('should reject NAT64 (64:ff9b::/96) embedding arbitrary IPv4', () => { + const result = SSRFProtection.validateUrlSync('http://[64:ff9b::a9fe:a9fe]'); + expect(result.valid).toBe(false); + expect(result.reason).toBe('IPv6 private/mapped address not allowed'); + }); + + it('should reject full fc00::/7 ULA range, not just fc00:/fd00: literals', () => { + // RFC 4193: fc00::/7 spans fc00-fdff in the first hextet. + const payloads = [ + 'http://[fcba::1]', // ULA outside literal fc00: + 'http://[fd12:3456::]', // ULA outside literal fd00: + ]; + for (const url of payloads) { + const result = SSRFProtection.validateUrlSync(url); + expect(result.valid, `url=${url}`).toBe(false); + expect(result.reason).toBe('IPv6 private/mapped address not allowed'); + } + }); + + it('should reject site-local fec0::/10 (deprecated, RFC 3879)', () => { + const result = SSRFProtection.validateUrlSync('http://[fec0::1]'); + expect(result.valid).toBe(false); + expect(result.reason).toBe('IPv6 private/mapped address not allowed'); + }); + + it('should not false-positive on public IPv6 addresses', () => { + // 2001:db8::/32 is the documentation range but is still parseable and + // public-routable from the validator's perspective; must NOT be blocked. + const publicPayloads = [ + 'http://[2001:db8::1]', + 'http://[2606:4700:4700::1111]', // Cloudflare + 'http://[2620:0:2d0:200::7]', + ]; + for (const url of publicPayloads) { + const result = SSRFProtection.validateUrlSync(url); + expect(result.valid, `url=${url}`).toBe(true); + } + }); + + it('should not false-positive on domain names starting with hex-like labels', () => { + // isPrivateOrMappedIpv6 gates on net.isIPv6; domain names with "fc"/"fd" + // labels must not be misclassified as ULA. + const domains = [ + 'http://fcexample.com', + 'http://fdexample.com', + 'http://fec0example.com', + ]; + for (const url of domains) { + const result = SSRFProtection.validateUrlSync(url); + expect(result.valid, `url=${url}`).toBe(true); + } + }); + + it('should not perform DNS resolution for IPv6 payloads', () => { + SSRFProtection.validateUrlSync('http://[::ffff:169.254.169.254]'); + SSRFProtection.validateUrlSync('http://[::ffff:127.0.0.1]:5678'); + SSRFProtection.validateUrlSync('http://[fe80::1]'); + SSRFProtection.validateUrlSync('http://[2002:a9fe:a9fe::]'); + SSRFProtection.validateUrlSync('http://[64:ff9b::a9fe:a9fe]'); + expect(vi.mocked(dns.lookup)).toHaveBeenCalledTimes(0); + }); + }); }); });
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/czlonkowski/n8n-mcp/commit/9639f757853149f0cb16663cc8b6b6468f27a25fnvdPatchWEB
- github.com/advisories/GHSA-56c3-vfp2-5qqjghsaADVISORY
- github.com/czlonkowski/n8n-mcp/security/advisories/GHSA-56c3-vfp2-5qqjnvdMitigationVendor AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-42449ghsaADVISORY
News mentions
0No linked articles in our index yet.