Every CVE Search/ Get API key

CVE-2026-125115

Novu: `validateUrlSsrf` permits CGNAT (100.64.0.0/10) destinations — affects Workflow HTTP request step + Webhook filter condition

6.8 MediumFix available

Novu provides an API for sending notifications through multiple channels. Prior to 3.17.0, validateUrlSsrf in libs/application-generic/src/utils/ssrf-url-validation.ts uses the isPrivateIp deny-list without covering the RFC6598 100.64.0.0/10 shared address space or its IPv4-mapped IPv6 forms. The workflow HTTP request step and webhook filter condition can therefore send server-side requests when a configured literal address or resolved hostname falls in that range. In Alibaba Cloud, 100.100.100.200 may expose instance metadata, while self-hosted and multi-tenant deployments may expose provider-internal services, private infrastructure, service meshes, or VPN resources reachable through CGNAT addressing. This issue is fixed in version 3.17.0.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
Required

What an attacker can affect

Confidentiality
High
Integrity
None
Availability
None

Exploit likelihood

0.27%Scored on 2026-11-10. The trend appears after a second daily score.

What each source reports

CVE List Score used 6.8

Novu: `validateUrlSsrf` permits CGNAT (100.64.0.0/10) destinations — affects Workflow HTTP request step + Webhook filter condition

Novu provides an API for sending notifications through multiple channels. Prior to 3.17.0, validateUrlSsrf in libs/application-generic/src/utils/ssrf-url-validation.ts uses the isPrivateIp deny-list without covering the RFC6598 100.64.0.0/10 shared address space or its IPv4-mapped IPv6 forms. The workflow HTTP request step and webhook filter condition can therefore send server-side requests when a configured literal address or resolved hostname falls in that range. In Alibaba Cloud, 100.100.100.200 may expose instance metadata, while self-hosted and multi-tenant deployments may expose provider-internal services, private infrastructure, service meshes, or VPN resources reachable through CGNAT addressing. This issue is fixed in version 3.17.0.

Record
CVE-2026-125115
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:N/A:N
Weakness
CWE-918
Updated
2026-11-09
EUVDEUVD-2026-120496 6.8

Novu provides an API for sending notifications through multiple channels. Prior to 3.17.0, validateUrlSsrf in libs/application-generic/src/utils/ssrf-url-validation.ts uses the isPrivateIp deny-list without covering the RFC6598 100.64.0.0/10 shared address space or its IPv4-mapped IPv6 forms. The workflow HTTP request step and webhook filter condition can therefore send server-side requests when a configured literal address or resolved hostname falls in that range. In Alibaba Cloud, 100.100.100.200 may expose instance metadata, while self-hosted and multi-tenant deployments may expose provider-internal services, private infrastructure, service meshes, or VPN resources reachable through CGNAT addressing. This issue is fixed in version 3.17.0.

Record
EUVD-2026-120496
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:N/A:N
Updated
2026-11-09
GitHub AdvisoriesGHSA-vg6v-j97m-h5xq 6.8

@novu/application-generic: `validateUrlSsrf` permits CGNAT (100.64.0.0/10) destinations — affects Workflow HTTP request step + Webhook filter condition

Hi Novu team,

Reporting an SSRF blocklist gap in the shared validateUrlSsrf guard. A complete self-contained reproduction is inlined below — copy the four files into a directory and run docker compose up, plus a single-file probe that runs against Node directly. Locally validated against HEAD 291817c.

Summary

Novu's shared SSRF guard validateUrlSsrf(url) is used before server-side requests to user-configured URLs. The guard resolves hostnames and blocks a regex list of private/reserved IP ranges, but it does not block 100.64.0.0/10 shared address space. As a result, Novu features protected by this guard can still send server-side requests to destinations such as 100.100.100.200 (Alibaba Cloud metadata service) and any other service reachable in 100.64.0.0/10.

Affected code

Guard:

  • libs/application-generic/src/utils/ssrf-url-validation.ts
    • isPrivateIp(...) regex list at lines 9-28
    • DNS resolution and address validation at lines 55-72

Product call-sites:

  • Workflow HTTP request step: apps/worker/src/app/workflow/usecases/send-message/execute-http-request-step.usecase.ts — calls validateUrlSsrf(url) at line 149, then uses HttpClientService to send the request.
  • Webhook filter condition: libs/application-generic/src/usecases/conditions-filter/conditions-filter.usecase.ts — calls validateUrlSsrf(child.webhookUrl) at line 265, then sends axios.post(child.webhookUrl, ...) at line 277.

HTTP client:

  • libs/application-generic/src/services/http-client/http-client.service.ts — uses got(gotOptions) at lines 120 and 142 after the preflight validation.

Root cause

The SSRF guard uses a hand-written regex deny-list:

/^0\.0\.0\.0$/i,
/^127\./,
/^10\./,
/^172\.(1[6-9]|2[0-9]|3[01])\./,
/^192\.168\./,
/^169\.254\./,
/^::ffff:127\./i,
/^::ffff:10\./i,
/^::ffff:172\.(1[6-9]|2[0-9]|3[01])\./i,
/^::ffff:192\.168\./i,
/^::ffff:169\.254\./i,
/^::1$/,
/^fc00:/i,
/^fe80:/i,

This list omits 100.64.0.0/10, also called shared address space or CGNAT. These addresses are not RFC1918 private addresses, but they are also not normal public-internet destinations. Cloud and infrastructure providers commonly use special-use address ranges for metadata and internal services; Alibaba Cloud metadata is available at 100.100.100.200.

Reproduction — Part 1: unit-level probe (no Docker required)

Save the following file and run with node novu_ssrf_guard_probe.js. The script replicates validateUrlSsrf from libs/application-generic/src/utils/ssrf-url-validation.ts verbatim (the isPrivateIp regex list is copied as-is) and tests several URL categories.

novu_ssrf_guard_probe.js

const dns = require('dns/promises');

function isPrivateIp(ip) {
  const privateRanges = [
    /^0\.0\.0\.0$/i,
    /^127\./,
    /^10\./,
    /^172\.(1[6-9]|2[0-9]|3[01])\./,
    /^192\.168\./,
    /^169\.254\./,
    /^::ffff:127\./i,
    /^::ffff:10\./i,
    /^::ffff:172\.(1[6-9]|2[0-9]|3[01])\./i,
    /^::ffff:192\.168\./i,
    /^::ffff:169\.254\./i,
    /^::1$/,
    /^fc00:/i,
    /^fe80:/i,
  ];
  return privateRanges.some((range) => range.test(ip));
}

async function validateUrlSsrf(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    return 'Invalid URL format.';
  }
  if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
    return `URL scheme "${parsed.protocol}" is not allowed.`;
  }
  const hostname = parsed.hostname.toLowerCase();
  const blockedHostnames = ['localhost', 'metadata.google.internal'];
  if (blockedHostnames.includes(hostname)) {
    return `Requests to "${hostname}" are not allowed.`;
  }
  let addresses;
  try {
    addresses = await dns.lookup(hostname, { all: true });
  } catch {
    return `Unable to resolve hostname "${hostname}".`;
  }
  for (const { address } of addresses) {
    if (isPrivateIp(address)) {
      return `Requests to private or reserved IP addresses are not allowed (resolved: ${address}).`;
  …
Record
GHSA-vg6v-j97m-h5xq
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:N/A:N
Weakness
CWE-918
Updated
2026-11-09
NVD 6.8

Novu provides an API for sending notifications through multiple channels. Prior to 3.17.0, validateUrlSsrf in libs/application-generic/src/utils/ssrf-url-validation.ts uses the isPrivateIp deny-list without covering the RFC6598 100.64.0.0/10 shared address space or its IPv4-mapped IPv6 forms. The workflow HTTP request step and webhook filter condition can therefore send server-side requests when a configured literal address or resolved hostname falls in that range. In Alibaba Cloud, 100.100.100.200 may expose instance metadata, while self-hosted and multi-tenant deployments may expose provider-internal services, private infrastructure, service meshes, or VPN resources reachable through CGNAT addressing. This issue is fixed in version 3.17.0.

Record
CVE-2026-125115
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:N/A:N
Weakness
CWE-918
Updated
2026-11-10
OSV 6.8

Novu: `validateUrlSsrf` permits CGNAT (100.64.0.0/10) destinations — affects Workflow HTTP request step + Webhook filter condition

Novu provides an API for sending notifications through multiple channels. Prior to 3.17.0, validateUrlSsrf in libs/application-generic/src/utils/ssrf-url-validation.ts uses the isPrivateIp deny-list without covering the RFC6598 100.64.0.0/10 shared address space or its IPv4-mapped IPv6 forms. The workflow HTTP request step and webhook filter condition can therefore send server-side requests when a configured literal address or resolved hostname falls in that range. In Alibaba Cloud, 100.100.100.200 may expose instance metadata, while self-hosted and multi-tenant deployments may expose provider-internal services, private infrastructure, service meshes, or VPN resources reachable through CGNAT addressing. This issue is fixed in version 3.17.0.

Record
CVE-2026-125115
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:N/A:N
Weakness
CWE-918
Updated
2026-11-10
OSVGHSA-vg6v-j97m-h5xq 6.8

@novu/application-generic: `validateUrlSsrf` permits CGNAT (100.64.0.0/10) destinations — affects Workflow HTTP request step + Webhook filter condition

Hi Novu team,

Reporting an SSRF blocklist gap in the shared validateUrlSsrf guard. A complete self-contained reproduction is inlined below — copy the four files into a directory and run docker compose up, plus a single-file probe that runs against Node directly. Locally validated against HEAD 291817c.

Summary

Novu's shared SSRF guard validateUrlSsrf(url) is used before server-side requests to user-configured URLs. The guard resolves hostnames and blocks a regex list of private/reserved IP ranges, but it does not block 100.64.0.0/10 shared address space. As a result, Novu features protected by this guard can still send server-side requests to destinations such as 100.100.100.200 (Alibaba Cloud metadata service) and any other service reachable in 100.64.0.0/10.

Affected code

Guard:

  • libs/application-generic/src/utils/ssrf-url-validation.ts
    • isPrivateIp(...) regex list at lines 9-28
    • DNS resolution and address validation at lines 55-72

Product call-sites:

  • Workflow HTTP request step: apps/worker/src/app/workflow/usecases/send-message/execute-http-request-step.usecase.ts — calls validateUrlSsrf(url) at line 149, then uses HttpClientService to send the request.
  • Webhook filter condition: libs/application-generic/src/usecases/conditions-filter/conditions-filter.usecase.ts — calls validateUrlSsrf(child.webhookUrl) at line 265, then sends axios.post(child.webhookUrl, ...) at line 277.

HTTP client:

  • libs/application-generic/src/services/http-client/http-client.service.ts — uses got(gotOptions) at lines 120 and 142 after the preflight validation.

Root cause

The SSRF guard uses a hand-written regex deny-list:

/^0\.0\.0\.0$/i,
/^127\./,
/^10\./,
/^172\.(1[6-9]|2[0-9]|3[01])\./,
/^192\.168\./,
/^169\.254\./,
/^::ffff:127\./i,
/^::ffff:10\./i,
/^::ffff:172\.(1[6-9]|2[0-9]|3[01])\./i,
/^::ffff:192\.168\./i,
/^::ffff:169\.254\./i,
/^::1$/,
/^fc00:/i,
/^fe80:/i,

This list omits 100.64.0.0/10, also called shared address space or CGNAT. These addresses are not RFC1918 private addresses, but they are also not normal public-internet destinations. Cloud and infrastructure providers commonly use special-use address ranges for metadata and internal services; Alibaba Cloud metadata is available at 100.100.100.200.

Reproduction — Part 1: unit-level probe (no Docker required)

Save the following file and run with node novu_ssrf_guard_probe.js. The script replicates validateUrlSsrf from libs/application-generic/src/utils/ssrf-url-validation.ts verbatim (the isPrivateIp regex list is copied as-is) and tests several URL categories.

novu_ssrf_guard_probe.js

const dns = require('dns/promises');

function isPrivateIp(ip) {
  const privateRanges = [
    /^0\.0\.0\.0$/i,
    /^127\./,
    /^10\./,
    /^172\.(1[6-9]|2[0-9]|3[01])\./,
    /^192\.168\./,
    /^169\.254\./,
    /^::ffff:127\./i,
    /^::ffff:10\./i,
    /^::ffff:172\.(1[6-9]|2[0-9]|3[01])\./i,
    /^::ffff:192\.168\./i,
    /^::ffff:169\.254\./i,
    /^::1$/,
    /^fc00:/i,
    /^fe80:/i,
  ];
  return privateRanges.some((range) => range.test(ip));
}

async function validateUrlSsrf(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    return 'Invalid URL format.';
  }
  if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
    return `URL scheme "${parsed.protocol}" is not allowed.`;
  }
  const hostname = parsed.hostname.toLowerCase();
  const blockedHostnames = ['localhost', 'metadata.google.internal'];
  if (blockedHostnames.includes(hostname)) {
    return `Requests to "${hostname}" are not allowed.`;
  }
  let addresses;
  try {
    addresses = await dns.lookup(hostname, { all: true });
  } catch {
    return `Unable to resolve hostname "${hostname}".`;
  }
  for (const { address } of addresses) {
    if (isPrivateIp(address)) {
      return `Requests to private or reserved IP addresses are not allowed (resolved: ${address}).`;
  …
Record
GHSA-vg6v-j97m-h5xq
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:N/A:N
Weakness
CWE-918
Updated
2026-11-09

Affected products

Affected packages

Affected packages
PackageAffectedFixed
@novu/application-generic
npm
before 3.17.0
3.17.0

History

History
RecordedChangedSources
referencesNVD, OSV
description, references, severity, titleCVE List
aliases, products, referencesEUVD, OSV
aliases, facts, idGitHub Advisories
first indexedGitHub Advisories, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-125115 is a medium vulnerability (CVSS 6.8) in @novu Application-generic, published July 28, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.27%, the 20th percentile. Fixed in @novu/application-generic 3.17.0.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open