GitHub Advisoryadvisory · npm
GHSA-8gr3-5j6f-25gp Novu: Stored XSS in In-App Inbox via notification redirect.url javascript: scheme
MEDIUM
Official page ↗
- Aliases
- CVE-2026-75510
- Ecosystem
- npm
- Packages
- @novu/js
- Severity
- Moderate
- CVSS vector
- CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N
- Weaknesses
- CWE-79
- Reviewed
- 2026-09-22
Affected packages
• npm @novu/js: < 3.18.0 → fixed in 3.18.0
Description
Summary
The `@novu/js` In-App Inbox renderer passes a notification's `redirect.url` to `window.open()` with no URL-scheme validation. The value originates from a notification's call-to-action and is delivered to the recipient verbatim.
An authenticated organization member (or any holder of the environment API key) creates a v1 in-app workflow whose step CTA stores `cta.data = { url: "javascript:", target: "_self" }`. The v1 message-template `cta.data` field is a Mongoose `Mixed` type, so the arbitrary `target` key is accepted and persisted. The server-side inbox mapper copies `cta.data.url` and `cta.data.target` into the notification's `redirect` object with no scheme check. Novu's v2 control schema validates redirect URLs against `redirectUrlRegex` (which rejects `javascript:`), proving the intended invariant; the v1 path and the client renderer do not enforce it.
When the recipient clicks the notification, the inbox calls `window.open(url, "_self", "noopener noreferrer")`. In Chromium browsers a `javascript:` URL opened with `target="_self"` executes in the current document origin (the default `_blank` is browser-blocked, so the attacker sets `_self`).
Result: a low-privilege content author runs arbitrary JavaScript in the browser of every recipient who clicks, in the origin that hosts the inbox (the customer application or the self-hosted Novu dashboard, neither of which sends a CSP).
Affected
novuhq/novu self-hosted and cloud, API
{"name":"poc","notificationGroupId":"","active":true,
"steps":[{"template":{"type":"in_app","content":"click me",
"cta":{"type":"redirect","data":{
"url":"javascript:window.top.__X=document.domain;void 0","target":"_self"}}}}]}
```
2. Trigger it to a subscriber, then read the feed with a subscriber token minted from the public application identifier (no secret):
```
POST /v1/inbox/session {"applicationIdentifier":"","subscriberId":""}
GET /v1/inbox/notifications Authorization: Bearer
-> redirect: {"url":"javascript:window.top.__X=document.domain;void 0","target":"_self"}
```
3. The subscriber clicks the notification in the `@novu/js` / `@novu/react` Inbox.
Live-verified: the stored `javascript:` URL is returned verbatim by the inbox feed, and the exact shipped `navigate()` logic invoked from a real click runs `window.open(url,"_self",...)`, executing the payload in the `http://:4000` origin (no CSP); `window.top.__X` was set to the page origin. On the self-hosted dashboard the test inbox bell uses `subscriberId = user.externalId` (apps/dashboard/src/components/inbox-button.tsx:102), so a member targets another member's id and the executing payload reads `localStorage['self-hosted-jwt']` (apps/dashboard/src/utils/self-hosted/jwt-manager.tsx:4), the dashboard session token.
Impact
• Stored XSS in the recipient's browser, cross-principal (content author to end-user subscriber), persistent, replicated to every recipient of the workflow.
• On the self-hosted dashboard origin (no CSP), theft of `localStorage['self-hosted-jwt']` yields takeover of another member or admin account.
• In a customer application embedding the inbox, session and token theft and authenticated actions in that origin.
• Triggered by the lowest privilege that can author a workflow, default config, one recipient click.
Credit
Jan Kahmen, turingpoint ([email protected])
Related
Alias · CVE (1)
- CVE-2026-75510 Novu provides an API for sending notifications through multiple channels. vulnerability
Weakness · CWE (1)
- CWE-79 Improper Neutralization of Input During Web Page Generation weakness
References
- https://github.com/novuhq/novu/security/advisories/GHSA-8gr3-5j6f-25gp web
- https://nvd.nist.gov/vuln/detail/CVE-2026-75510 advisory
- https://github.com/novuhq/novu/pull/11453 web
- https://github.com/novuhq/novu/commit/f105f3d41a4405a75f803634d49f15a967524d8a web
- https://github.com/novuhq/novu package
- https://github.com/novuhq/novu/releases/tag/v3.18.0 web