CVE-2026-26862
Description
CleverTap Web SDK version 1.15.2 and earlier is vulnerable to DOM-based Cross-Site Scripting (XSS) via window.postMessage in the Visual Builder module. The origin validation in src/modules/visualBuilder/pageBuilder.js (lines 56-60) uses the includes() method to verify the originUrl contains "dashboard.clevertap.com", which can be bypassed by an attacker using a crafted subdomain
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CleverTap Web SDK ≤1.15.2 contains a DOM-based XSS vulnerability in its Visual Builder module due to insufficient window.postMessage origin validation with includes().
CleverTap Web SDK versions 1.15.2 and earlier are vulnerable to a DOM-based Cross-Site Scripting (XSS) in the Visual Builder module. The root cause lies in the origin validation logic within src/modules/visualBuilder/pageBuilder.js (lines 56-60). The code uses JavaScript's includes() method to check whether the originUrl property of a postMessage event contains the string "dashboard.clevertap.com". This approach is fundamentally flawed because includes() performs a substring match, not an exact hostname check, allowing an attacker to craft a malicious subdomain such as "evil-dashboard.clevertap.com" that still passes the validation [1][2][3].
An attacker can exploit this by embedding a malicious window.postMessage event from an attacker-controlled subdomain. The Visual Builder module listens for postMessage events and processes the originUrl parameter without further verification. Since the flawed check only confirms the presence of the string rather than the actual origin, any site whose domain contains dashboard.clevertap.com (including subdomains an attacker can register or control) can send messages that are accepted as trusted. No user interaction beyond visiting a page that loads the CleverTap SDK is required, making it a low-complexity attack vector [2][4].
The impact is a DOM-based Cross-Site Scripting (XSS) vulnerability, allowing an attacker to execute arbitrary JavaScript in the context of the victim's session. This could lead to data theft, session hijacking, or unauthorized actions performed on behalf of the user. The CVSS vector has not been fully assessed by NVD at this time, but the vulnerability is classified as high severity due to the ease of exploitation and the potential for full compromise of the user's interaction with the application [3].
CleverTap addressed the vulnerability in version 1.15.3 by implementing a proper origin check that validates the exact origin of the postMessage sender against the expected CleverTap dashboard URL, removing the flawed substring-based check. Users are strongly advised to upgrade to version 1.15.3 or later. No workaround is documented for earlier versions. The fix is visible in the commit referenced as Pull Request #417 [2][4].
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
clevertap-web-sdknpm | < 1.15.3 | 1.15.3 |
Affected products
2- CleverTap/Web SDKdescription
Patches
284695b726a75[WEB-3755] Strict Domain Check
6 files changed · +17 −11
clevertap.js+7 −4 modified@@ -11643,11 +11643,10 @@ const handleMessageEvent = event => { if (event.data && isValidUrl(event.data.originUrl)) { - const msgOrigin = new URL(event.data.originUrl).origin; // Visual Editor is opened from only dashboard, while preview can be opened from both dashboard & Visual Editor + // Visual Editor is opened from only dashboard, while preview can be opened from both dashboard & Visual Editor // therefore adding check for self origin // Visual Editor can only be opened in their domain not inside dashboard - - if (!event.origin.includes(WVE_URL_ORIGIN.CLEVERTAP) && !event.origin.includes(window.location.origin) || event.origin !== msgOrigin) { + if (!event.origin.endsWith(WVE_URL_ORIGIN.CLEVERTAP) && !event.origin.endsWith(window.location.origin)) { return; } } else { @@ -12488,7 +12487,7 @@ }; function handleCustomHtmlPreviewPostMessageEvent(event, logger) { - if (!event.origin.includes(WVE_URL_ORIGIN.CLEVERTAP)) { + if (!event.origin.endsWith(WVE_URL_ORIGIN.CLEVERTAP)) { return; } @@ -13328,6 +13327,10 @@ httpsIframe.setAttribute('src', httpsIframePath); document.body.appendChild(httpsIframe); window.addEventListener('message', event => { + if (!event.origin.endsWith(WVE_URL_ORIGIN.CLEVERTAP)) { + return; + } + if (event.data != null) { let obj = {};
clevertap.js.map+1 −1 modifiedclevertap.min.js+1 −1 modifiedsrc/modules/notification.js+4 −0 modified@@ -17,6 +17,7 @@ import { import { setNotificationHandlerValues, processSoftPrompt } from './webPushPrompt/prompt' import { isChrome, isFirefox, isSafari } from '../util/helpers' +import { WVE_URL_ORIGIN } from './visualBuilder/builder_constants' export default class NotificationHandler extends Array { #oldValues @@ -517,6 +518,9 @@ export default class NotificationHandler extends Array { httpsIframe.setAttribute('src', httpsIframePath) document.body.appendChild(httpsIframe) window.addEventListener('message', (event) => { + if (!event.origin.endsWith(WVE_URL_ORIGIN.CLEVERTAP)) { + return + } if (event.data != null) { let obj = {} try {
src/modules/visualBuilder/pageBuilder.js+3 −4 modified@@ -49,14 +49,13 @@ export const handleActionMode = (_logger, accountId) => { const handleMessageEvent = (event) => { if (event.data && isValidUrl(event.data.originUrl)) { - const msgOrigin = new URL(event.data.originUrl).origin // Visual Editor is opened from only dashboard, while preview can be opened from both dashboard & Visual Editor // therefore adding check for self origin // Visual Editor can only be opened in their domain not inside dashboard + if ( - (!event.origin.includes(WVE_URL_ORIGIN.CLEVERTAP) && - !event.origin.includes(window.location.origin)) || - event.origin !== msgOrigin + !event.origin.endsWith(WVE_URL_ORIGIN.CLEVERTAP) && + !event.origin.endsWith(window.location.origin) ) { return }
src/util/campaignRender/nativeDisplay.js+1 −1 modified@@ -116,7 +116,7 @@ export const handleJson = (targetingMsgJson) => { } function handleCustomHtmlPreviewPostMessageEvent (event, logger) { - if (!event.origin.includes(WVE_URL_ORIGIN.CLEVERTAP)) { + if (!event.origin.endsWith(WVE_URL_ORIGIN.CLEVERTAP)) { return } const eventData = JSON.parse(event.data)
766f75f0c908Added origin check in postMessage (#417)
8 files changed · +31 −13
CHANGELOG.md+3 −0 modified@@ -1,6 +1,9 @@ # Change Log All notable changes to this project will be documented in this file. +## [1.15.2] 22nd May, 2025 +- Added origin check in postMessage for solve XSS vulnerability + ## [1.15.1] 20th May, 2025 - Bug fix for Webpopup Preview
clevertap.js+15 −7 modified@@ -11623,7 +11623,7 @@ case WVE_QUERY_PARAMS.SDK_CHECK: if (parentWindow) { logger$1.debug('SDK version check'); - const sdkVersion = '1.15.1'; + const sdkVersion = '1.15.2'; parentWindow.postMessage({ message: 'SDKVersion', accountId, @@ -11639,13 +11639,15 @@ break; } } - }; // TODO: Add a guarding mechanism to skip postMessages from non trusted sources + }; const handleMessageEvent = event => { if (event.data && isValidUrl(event.data.originUrl)) { - const msgOrigin = new URL(event.data.originUrl).origin; + const msgOrigin = new URL(event.data.originUrl).origin; // Visual Editor is opened from only dashboard, while preview can be opened from both dashboard & Visual Editor + // therefore adding check for self origin + // Visual Editor can only be opened in their domain not inside dashboard - if (!event.origin.includes(WVE_URL_ORIGIN.CLEVERTAP) && !event.origin.includes(window.location.origin) && !event.origin.includes(WVE_URL_ORIGIN.LOCAL) || event.origin !== msgOrigin) { + if (!event.origin.includes(WVE_URL_ORIGIN.CLEVERTAP) && !event.origin.includes(window.location.origin) || event.origin !== msgOrigin) { return; } } else { @@ -12486,6 +12488,10 @@ }; function handleCustomHtmlPreviewPostMessageEvent(event, logger) { + if (!event.origin.includes(WVE_URL_ORIGIN.CLEVERTAP)) { + return; + } + const eventData = JSON.parse(event.data); const inAppNotifs = eventData.inapp_notifs; const msgContent = inAppNotifs[0].msgContent; @@ -13221,7 +13227,9 @@ if (typeof navigator.serviceWorker === 'undefined') { return; - } + } // Used for Shopify Web Push mentioned here + // (https://wizrocket.atlassian.net/wiki/spaces/TAMKB/pages/1824325665/Implementing+Web+Push+in+Shopify+if+not+using+the+Shopify+App+approach) + const isHTTP = httpsPopupPath != null && httpsIframePath != null; // make sure the site is on https for chrome notifications @@ -15352,7 +15360,7 @@ let proto = document.location.protocol; proto = proto.replace(':', ''); dataObject.af = { ...dataObject.af, - lib: 'web-sdk-v1.15.1', + lib: 'web-sdk-v1.15.2', protocol: proto, ...$ct.flutterVersion }; // app fields @@ -17201,7 +17209,7 @@ } getSDKVersion() { - return 'web-sdk-v1.15.1'; + return 'web-sdk-v1.15.2'; } defineVariable(name, defaultValue) {
clevertap.js.map+1 −1 modifiedclevertap.min.js+1 −1 modifiedpackage.json+1 −1 modified@@ -1,6 +1,6 @@ { "name": "clevertap-web-sdk", - "version": "1.15.1", + "version": "1.15.2", "description": "", "main": "clevertap.js", "scripts": {
src/modules/notification.js+2 −0 modified@@ -426,6 +426,8 @@ export default class NotificationHandler extends Array { return } + // Used for Shopify Web Push mentioned here + // (https://wizrocket.atlassian.net/wiki/spaces/TAMKB/pages/1824325665/Implementing+Web+Push+in+Shopify+if+not+using+the+Shopify+App+approach) const isHTTP = httpsPopupPath != null && httpsIframePath != null // make sure the site is on https for chrome notifications
src/modules/visualBuilder/pageBuilder.js+4 −3 modified@@ -47,14 +47,15 @@ export const handleActionMode = (_logger, accountId) => { } } -// TODO: Add a guarding mechanism to skip postMessages from non trusted sources const handleMessageEvent = (event) => { if (event.data && isValidUrl(event.data.originUrl)) { const msgOrigin = new URL(event.data.originUrl).origin + // Visual Editor is opened from only dashboard, while preview can be opened from both dashboard & Visual Editor + // therefore adding check for self origin + // Visual Editor can only be opened in their domain not inside dashboard if ( (!event.origin.includes(WVE_URL_ORIGIN.CLEVERTAP) && - !event.origin.includes(window.location.origin) && - !event.origin.includes(WVE_URL_ORIGIN.LOCAL)) || + !event.origin.includes(window.location.origin)) || event.origin !== msgOrigin ) { return
src/util/campaignRender/nativeDisplay.js+4 −0 modified@@ -3,6 +3,7 @@ import { CTWebPersonalisationBanner } from '../web-personalisation/banner' import { CTWebPersonalisationCarousel } from '../web-personalisation/carousel' import { addScriptTo, appendScriptForCustomEvent } from '../campaignRender/utilities' +import { WVE_URL_ORIGIN } from '../../modules/visualBuilder/builder_constants' export const renderPersonalisationBanner = (targetingMsgJson) => { if (customElements.get('ct-web-personalisation-banner') === undefined) { @@ -115,6 +116,9 @@ export const handleJson = (targetingMsgJson) => { } function handleCustomHtmlPreviewPostMessageEvent (event, logger) { + if (!event.origin.includes(WVE_URL_ORIGIN.CLEVERTAP)) { + return + } const eventData = JSON.parse(event.data) const inAppNotifs = eventData.inapp_notifs const msgContent = inAppNotifs[0].msgContent
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-jfrq-hj9f-c8qxghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-26862ghsaADVISORY
- github.com/CleverTap/clevertap-web-sdk/blob/cf1b65d/src/modules/visualBuilder/pageBuilder.jsghsaWEB
- github.com/CleverTap/clevertap-web-sdk/commit/766f75f0c9082a27eb0b59c9fa4b0d9b19ba3d10ghsaWEB
- github.com/CleverTap/clevertap-web-sdk/commit/84695b726a751614ddc3a4f71382c239c5833e03ghsaWEB
- github.com/CleverTap/clevertap-web-sdk/issues/442ghsaWEB
- github.com/CleverTap/clevertap-web-sdk/pull/417ghsaWEB
News mentions
0No linked articles in our index yet.