Is WASender Free Bulk Messaging Plugin safe?

Medium risk

Send bulk WhatsApp messages to multiple contacts without saving each number first.

This extension lets users add contacts manually or upload an Excel sheet, then type and send WhatsApp messages in bulk. The store description says Pro features add personalized messages, attachments, batching and time-gap controls, contact download, filtering, and message reports.

wasender-main-devsv1.0.79Chrome Web Store
47Risk
Who publishes it

wasender-main-devs - no other listings under this identity, 3 shared hostnames

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
wasender-main-devs

Shared hosts - 3 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

joyz.ai
Also called by 3 other listings: WASender, B Tools, WF Tools – CRM Utility
next-api.telecrm.in
Also called by 3 other listings: WASender, B Tools, WF Tools – CRM Utility
wasender.com
Also called by 3 other listings, including WASender

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Account setup details are posted to TeleCRM

First-time setup collects your WhatsApp name, phone, email, country, and account phone into CRM lead records.

The source posts to a TeleCRM endpoint and an AWS Gateway endpoint, each with an embedded auth value.

No live POST was captured.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You submit the account setup form after entering your name, WhatsApp number, and email address.

The first-signup path runs after the extension saves those details.

The extension did this

The extension creates a CRM lead from those account details and prepares POST requests with embedded authorization values.

The source contains both a TeleCRM route and an AWS API Gateway CRM route for lead creation.

Fields the source maps into CRM lead records
  • Your account name
    Aman Gupta

    This identifies the person or business account using the extension.

  • Your signup phone number
    +91 8873520027

    This is the WhatsApp number you enter during setup, including country information.

  • Your email address
    aman.gupta@example.in

    This can identify and contact you outside the extension.

  • Your country selection
    India / IN / +91

    This adds location context to the account record sent during setup.

  • Your extension account phone
    +91 9027172291

    This links the CRM lead to the phone number already associated with the extension account.

  • Embedded CRM authorization
    Bearer <redacted>

    The request includes a bundled authorization value rather than prompting you to approve a separate CRM connection.

Captured request
POSThttps://next-api.telecrm.in/enterprise/60a51c74589084cee99c1903/autoupdatelead

No live response was captured; the POST URL and method are reconstructed from the shipped service-worker source.

Headers
Content-Type
application/json
Authorization
Bearer <redacted>
Captured request
POSThttps://6vokqgdnu4.execute-api.ap-south-1.amazonaws.com/enterprise/67585858ed44a90bb3662ee1/entities

No live response was captured; the POST URL and method are reconstructed from the shipped main bundle source.

Headers
Content-Type
application/json
Authorization
AdminBearer <redacted>
The code that does this

The first-signup path and CRM POST helpers

Readable version

First-time signup calls lead creation

deobfuscated/969.js
handleFirstTimeSignup() {  var e = this;  return (0, l.Z)(function*() {    try {      yield e.log.getCookies(), e.log.createLead({        fields: e.log.getCookies(),        actiontexts: ["Signup done", JSON.stringify(e.log.getCookies(), null, 2)]      })    } catch (o) {      console.error("Error in creating lead", o)    }    try {      e.extDataService.getAuth()?.isPro() || (yield e.userService.activateFreePlan(), setTimeout(() => {        e.uiService.refresh()      }, 0))    } catch (o) {      console.error("Error in activating free plan", o)    }  })()}

Log wrapper copies extension account details into leadService.createLead

deobfuscated/main.js
createLead(pt) {  var it = this;  return (0, h.Z)(function*() {    const lt = it.extAuth.currentUser.phoneNumber,      It = it.extAuth.currentUser.name,      Ut = it.extAuth.getUserDetails()?.email,      Ot = it.extAuth.getUserDetails()?.countryInfo,      xn = it.extAuth.getUserDetails()?.number;    return it.leadService.createLead({      name: It,      phoneNumber: xn,      email: Ut,      countryInfo: Ot,      alternatePhone: lt    })  })()}

Lead service builds the CRM entity and POSTs to AWS API Gateway

deobfuscated/main.js
createLead(pt) {  return (0, h.Z)(function*() {    const it = [{      fieldId: "contact_name",      type: "TEXT",      valueText: pt.name    }, {      fieldId: "contact_phone",      type: "PHONE",      valuePhone: {        countryInfo: pt.countryInfo || {          name: "India",          code: "IN",          dial_code: "91"        },        phoneNumber: pt.phoneNumber      }    }, {      fieldId: "contact_status",      type: "DROPDOWN",      valueDropdown: "New lead"    }, {      fieldId: "contact_source",      type: "DROPDOWN",      valueDropdown: "WACA"    }];    pt.email && it.push({      fieldId: "contact_email",      type: "EMAIL",      valueEmail: pt.email    }), pt.alternatePhone && it.push({      fieldId: "1_TEXT",      type: "TEXT",      valueText: pt.alternatePhone    });    const lt = {      id: "",      eId: H,      entityType: "CONTACT",      fields: it,      parentEntityId: "",      parentEntityType: null,      activities: [],      createdBy: "",      lastSeenTimestamp: 0,      creationTimestamp: Date.now(),      modificationTimestamp: Date.now()    };    try {      const It = yield fetch(`https://6vokqgdnu4.execute-api.ap-south-1.amazonaws.com/enterprise/${H}/entities`, {        method: "POST",        headers: {          "Content-Type": "application/json",          Authorization: "AdminBearer <redacted>"        },        body: JSON.stringify({          entity: lt        })      });      if (!It.ok) {        const Ot = yield It.json();        throw new Error(`HTTP ${It.status}: ${Ot.message||"Unknown error"}`)      }      return yield It.json()    } catch (It) {      console.error("Error creating contact:", It)    }  })()}

Service worker POSTs lead properties to TeleCRM

deobfuscated/service-worker.js
static createLead(e) {  var a = this;  return o(function*() {    return a.httpPost(`${y_CRM_ROUTE}`, e, {      Authorization: "Bearer <redacted>"    })  })()}

Message route exposes the service-worker lead creation call

deobfuscated/service-worker.js
case t.API_CREATE_LEAD:  return g.createLead(i.data.properties).then(n => a(n)).catch(n => {    console.error("Error in API_CREATE_LEAD:", n), a(null)  }), !0
External CRM destinations referenced by the shipped code
    • next-api.telecrm.in

    TeleCRM endpoint used by the service worker to receive lead properties from the extension.

    • 6vokqgdnu4.execute-api.ap-south-1.amazonaws.com

    AWS API Gateway endpoint used by the main bundle to create CRM contact entities.

Configured campaign cookies are added to analytics events

After an authenticated session on WhatsApp Web, the extension reads wasender.com cookies (origin, clickid, campaignid) as Amplitude properties sent to its cloud endpoint.

Logged-out POSTs show empty properties; authentication was untested.

Severity
Low unwanted
Type
Unexpected
CWE
CWE-522
Source
Dynamic sandbox
What actually happens
You did this

You open the extension while a WhatsApp Web session is available.

The cookie collection path runs during the extension refresh after it loads account and app configuration data.

The extension did this

The extension reads configured wasender.com cookie names and stores any returned values as analytics properties.

Later Amplitude events include those properties in the userProperties object sent to the cloud function.

Cookie fields configured for analytics properties
  • Campaign origin cookie
    origin=google (illustrative)

    This can connect your extension activity to a campaign or referral source if the cookie is present.

  • Click attribution cookie
    clickid=CjwKCAjwq7fABhB2EiwAwk-Yb9mE2 (illustrative)

    This can tie your extension session to a specific advertising or referral click if the cookie is present.

  • Campaign ID cookie
    campaignid=summer_launch_2026 (illustrative)

    This can add campaign context to analytics events associated with your extension session.

Captured request
POSThttps://us-central1-waplugin-34798.cloudfunctions.net/stats/log-amplitude

Dynamic analysis observed logged-out analytics posts with empty userProperties.

Body
{eventType:logged_out,userId:null,userProperties:{}}
The code that does this

Cookie values are merged into Amplitude userProperties

Readable version

Default and remotely configurable cookie names

main.js
constructor(b, H, ee, De, be, $e = Q.BUY_FRAME_DEFAULT_URL, Xe = !1, Ht = 10, pt, it, lt) {  this.DEFAULT_COOKIES = ["origin", "clickid", "campaignid"], this.reviewMode = !1, this.freeMessageLimit = 10, this.waSenderCookies = this.DEFAULT_COOKIES, this.suggestBan = b, this.banner = H, this.bannerIframeLink = ee, this.helpNumber = De, this.buyFrameLink = $e, this.reviewMode = Xe, this.freeMessageLimit = Ht, this.waSenderCookies = pt, this.autoRenewalLink = it, this.manageRenewalLink = lt, this.onBoardingFrameURL = be}static fromJSON(b) {  let H = null;  return b && (H = new Q({}, {}, "", "", "", Q.BUY_FRAME_DEFAULT_URL, !1, 10, [], "", ""), H.suggestBan = b.suggestBan ? b.suggestBan : {}, H.banner = b.banner ? b.banner : {}, H.bannerIframeLink = b.bannerIframeLink ? b.bannerIframeLink : "", H.helpNumber = b.helpNumber ? b.helpNumber : "", H.buyFrameLink = b.buyFrameLink ? b.buyFrameLink : Q.BUY_FRAME_DEFAULT_URL, H.reviewMode = !!b.reviewMode && b.reviewMode, H.freeMessageLimit = b.freeMessageLimit ? b.freeMessageLimit : 10, H.waSenderCookies = b.waSenderCookies ? b.waSenderCookies : H.DEFAULT_COOKIES, H.autoRenewalLink = b.autoRenewalLink ? b.autoRenewalLink : "", H.manageRenewalLink = b.manageRenewalLink ? b.manageRenewalLink : "", H.onBoardingFrameURL = b.onBoardingFrameURL ? b.onBoardingFrameURL : ""), H}

Cookie source URL constant

main.js
const q = {  APP_SERVER_ROUTER: "https://wasender.ai/api/b1",  APP_SERVER: "http://wasender.ai/api",  CLIENT_URL: "https://wasender.com",  SAFE_AND_MONTERING: "6558718ad99bc6ef5c706a94",  NEW_CUSTOMER: "6284dde8d771c4000970f6bc",  TEMPLATE_ID_SAFE_AND_MONTERING: "6558718ad99bc6ef5c706a97",  TEMPLATE_ID_NEW_CUSTOMER: "64c2a9c678aa832f68e69a2f",  BAN_PREVENT_LINK: "https://wasender.ai/banning-tips-ai?send=true",  ROOT_LAMBDA_URL: "http://localhost:3000",  ROOT_URL: "https://us-central1-waplugin-34798.cloudfunctions.net",  DATA_S3: "https://wasender-data.s3.ap-south-1.amazonaws.com",  INDEX_DB_NAME: "wanew",  CRM_ROUTE: "https://next-api.telecrm.in/enterprise/60a51c74589084cee99c1903/autoupdatelead",  ROOT_LAMBDA_URL: "https://3fsfla67nmbaykipymj7qlsahy0uymzo.lambda-url.ap-south-1.on.aws",  SELECTOR_URL: "https://us-central1-waplugin-34798.cloudfunctions.net/stats/adds",  SUPPORT_NUMBER: "919528066739",  chatSyncBatchSize: 50}

Chrome cookie read helper

main.js
getCookie(be, $e) {  return new Promise((Xe, Ht) => {    chrome.cookies.get({      url: be,      name: $e    }, pt => {      chrome.runtime.lastError ? (console.error(chrome.runtime.lastError), Ht(chrome.runtime.lastError)) : Xe({        value: pt?.value,        name: $e      })    })  })}

Configured cookies copied into analytics properties

main.js
getWaSenderCookie() {  var x = this;  return (0, Q.Z)(function*() {    try {      const de = wt.N.CLIENT_URL,        We = x.extDataService.appConfig.waSenderCookies.map(Kt => x.waService.getCookie(de, Kt)),        Ke = yield Promise.all(We), ft = {};      Ke.forEach(Kt => {        Kt?.value && (ft[Kt.name] = Kt.value)      }), x.log.setUniversalUserProperties(ft)    } catch (K) {      console.error("Error in getting cookies", K)    }  })()}

Universal properties included in log payload

main.js
setUniversalUserProperties(pt) {  Xe.UNIVERSAL_USER_PROPERTIES = {    ...Xe.UNIVERSAL_USER_PROPERTIES,    ...pt  }}log(pt, it = {}, lt = {}) {  var It = this;  return (0, h.Z)(function*() {    const Ut = It.extAuth.currentUser.phoneNumber,      Ot = k.nB.getManifestData(),      wt = {        ...Object.fromEntries(Object.entries(it).map(([Mt, Rt]) => [Mt, String(Rt)])),        version: String(Ot?.version),        isPro: String(It.extAuth.isPro())      },      Nt = {        extUserName: String(It.extAuth.currentUser.name),        extPhoneNumber: String(It.extAuth.currentUser.phoneNumber),        phoneNumber: It.extAuth.getUserDetails()?.number,        signupNumber: `${It.extAuth.getUserDetails()?.countryInfo?.dial_code}${It.extAuth.getUserDetails()?.number}`,        email: It.extAuth.getUserDetails()?.email,        name: It.extAuth.getUserDetails()?.name,        countryCode: It.extAuth.getUserDetails()?.countryInfo?.name,        getTips: String(It.extAuth.getUserDetails()?.getTips),        installTimestamp: String(It.extAuth.getUserDetails()?.installTimestamp),        verifiedEmail: String(It.extAuth.getUserDetails()?.verifiedEmail),        isPro: String(It.extAuth.isPro()),        ...Xe.UNIVERSAL_USER_PROPERTIES      };    yield k.bl.logAmplitude(pt, Ut, wt, Nt)  })()}

Service worker POST to cloud function

service-worker.js
static logAmplitude(e, a, r, n) {  var E = this;  return o(function*() {    const m = `${y_ROOT_URL}/stats/log-amplitude`;    yield E.httpPost(m, {      eventType: e,      userId: a,      eventProperties: r,      userProperties: n    }, {      Authorization: `Bearer ${a}`,      Version: E.getVersion()    })  })()}
Analytics destination
    • us-central1-waplugin-34798.cloudfunctions.net

    Receives Amplitude event payloads from the extension, including the userProperties object populated by the cookie-reading code path.

Updated 30 September 2026heogilejknffekkbjdjmoamaehdblmnc