VYPR
High severityNVD Advisory· Published Feb 27, 2026· Updated Feb 27, 2026

CVE-2026-26862

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.

PackageAffected versionsPatched versions
clevertap-web-sdknpm
< 1.15.31.15.3

Affected products

2

Patches

2
84695b726a75

[WEB-3755] Strict Domain Check

https://github.com/CleverTap/clevertap-web-sdkKunal SInghMay 28, 2025via ghsa
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 modified
  • clevertap.min.js+1 1 modified
  • src/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)
    
766f75f0c908

Added origin check in postMessage (#417)

https://github.com/CleverTap/clevertap-web-sdkKunal SInghMay 22, 2025via ghsa
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 modified
  • clevertap.min.js+1 1 modified
  • package.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

News mentions

0

No linked articles in our index yet.