Novu has a XSS sanitization bypass
Date: 2026-04
Affected: npm/novu/api
Attack vector: xss
Category: vulnerability-disclosure
Severity: High
CWE: CWE-79
CVSS: 8.1 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N)

### Summary XSS sanitization is incomplete, some attributes are missing such as `oncontentvisibilityautostatechange=`. This allows for the email preview to render HTML that executes arbitrary JavaScript, ### Details Sanitization is implemented here: https://github.com/novuhq/novu/blob/next/libs/application-generic/src/services/sanitize/sanitizer.service.ts With `allowedAttributes: false`, all attributes are allowed through `sanitize-html`. Even dangerous ones like `oncontentvisibilityautostatechange=`. The `DANGEROUS_ATTRIBUTES` array tries to handle this by denying more attributes after the fact, but this list is incomplete. I copied all well-known payloads from: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet And found that the `oncontentvisibilityautostatechange=` attribute isn't detected. PS. there seems to also be another even more lax sanitizer here, but I wasn't able to figure out where it is used: https://github.com/novuhq/novu/blob/next/packages/framework/src/utils/sanitize.utils.ts ### PoC 1. Create a new workflow and add an *Email* step 2. In the body, write the following HTML code: ```html <a oncontentvisibilityautostatechange="alert(window.origin)" style="display:block;content-visibility:auto"> ``` 3. Wait a second and notice the XSS popup showing the current origin: <img width="1515" height="610" alt="image" src="https://github.com/user-attachments/assets/7d519a50-3bed-4f04-b78c-9c5938717433" /> https://dashboard.novu.co/env/dev


Mitigations:

Architectural layers (MAESTRO):