CVE-2026-125114High· 8.3▾ TwilightNovu has SSRF via conditions filter webhook bypasses validateUrlSsrf() protection
▾ Twilight zone — High severity, or a signal on a lesser flaw
impact 45.7 · likelihood 0 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
Disclosure to exploitation, from the record and what we observed since indexing it.
Disclosed via GHSA
Last analysed / modified upstream
The conditions filter webhook at libs/application-generic/src/usecases/conditions-filter/conditions-filter.usecase.ts line 261 sends POST requests to user-configured URLs using raw axios.post() with no SSRF validation. The HTTP Request workflow step in the same codebase correctly uses validateUrlSsrf() which blocks private IP ranges. The conditions webhook was not included in this protection.
conditions-filter.usecase.ts line 261:
return await axios.post(child.webhookUrl, payload, config).then((response) => {
return response.data as Record<string, unknown>;
});
No call to validateUrlSsrf(). The webhookUrl comes from the workflow condition configuration with zero validation.
execute-http-request-step.usecase.ts line 130:
const ssrfValidationError = await validateUrlSsrf(url);
if (ssrfValidationError) {
// blocked
}
This function resolves DNS and checks against private ranges (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16). It exists in the codebase but is not applied to the conditions webhook path.
http://169.254.169.254/latest/meta-data/iam/security-credentials/axios.post() to the metadata endpointFull-read SSRF. The response body is returned as Record<string, unknown> for condition evaluation and stored in the execution details raw field. The GET /execution-details API returns this data.
The POST method limits some metadata endpoints (GCP requires GET, Azure requires GET), but AWS IMDSv1 accepts POST and returns credentials. Internal services accepting POST are also reachable.
Extract validateUrlSsrf() to a shared utility and call it before the axios.post in conditions-filter.usecase.ts:
const ssrfError = await validateUrlSsrf(child.webhookUrl);
if (ssrfError) {
throw new Error('Webhook URL blocked by SSRF protection');
}
return await axios.post(child.webhookUrl, payload, config)...
@novu/api < 3.15.0Upgrade to a patched release:
@novu/api 3.15.0Connected by shared product, vendor, weakness, or advisory.
CVE-2026-125115Medium· 6.8@novu/application-generic: `validateUrlSsrf` permits CGNAT (100.64.0.0/10) destinations — affects Workflow HTTP request step + Webhook filter condition
GHSA-vg6v-j97m-h5xqMedium· 6.8@novu/application-generic: `validateUrlSsrf` permits CGNAT (100.64.0.0/10) destinations — affects Workflow HTTP request step + Webhook filter condition
CVE-2025-68616High· 7.5WeasyPrint helps web developers to create PDF documents
CVE-2025-61916High· 7.9Spinnaker is an open source, multi-cloud continuous delivery platform
CVE-2025-66201High· 8.1LibreChat is a ChatGPT clone with additional features
CVE-2021-21985Critical· 9.8The vSphere Client (HTML5) contains a remote code execution vulnerability due to lack of input validation in the Virtual SAN Health Check plug-in which is enabled by default in vCenter Server