Is WA Bulk Message Sender safe?
WA Bulk Message Sender is medium risk. Once linked, the extension reads your WhatsApp number from storage and sends it in analytics to Alibaba, with user agent, install time, and a persistent UUID. A marker login appeared in s_user_phone_num, plus a derived country code.…
Who publishes itbulk-sender - no other listings under this identity, 5 shared hostnames
bulk-sender - no other listings under this identity, 5 shared hostnames
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 5 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.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
WhatsApp Phone Number Sent to Alibaba Cloud Logging Endpoint
Once linked, the extension reads your WhatsApp number from storage and sends it in analytics to Alibaba, with user agent, install time, and a persistent UUID.
A marker login appeared in s_user_phone_num, plus a derived country code.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You link a WhatsApp account to the extension and continue using it normally.
The extension's own login detection picks up the phone number tied to your WhatsApp Web session and stores it locally.
The next time the extension logs any analytics event, your phone number is included in a payload sent to an Alibaba Cloud logging endpoint.
The request also carries a country code derived from the phone number, your browser's user agent, install time, and a persistent install UUID.
- WhatsApp phone number14155552671
The phone number linked to your WhatsApp Web session, sent as plain text.
- Country codeUS
A country code the extension derives from your phone number using a phone-number-parsing library.
- Install UUID3f1b2e7a-9c4d-4a2b-8e31-6a2f9d0c1234
A persistent identifier for your install lets Alibaba Cloud tie every event back to the same browser over time.
- User agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Your browser and OS details, sent with every event.
- Install time1756540700
When the extension was first installed on this browser.
200 OK. Observed during dynamic analysis: a planted marker value written to chrome.storage.local's userPhoneNum key was reflected verbatim in this request's s_user_phone_num field.
- Content-Type
- application/x-protobuf
- x-log-apiversion
- 0.6.0
{ "__logs__": [ { "event_source": "9", "event_type": "900008", "event_time": "1756540800", "install_time": "1756540700", "platform": "win32", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "uuid": "3f1b2e7a-9c4d-4a2b-8e31-6a2f9d0c1234", "version": "1.3.12", "language": "en-US", "s_user_phone_num": "14155552671", "s_country_code": "US" } ]}Log-payload builder, shipped vs. deobfuscated
dealLog / function b -- reads storage, builds payload, sends
background.js:3554-3617function b(t) { if (a) return; let e = t.eventType, n = t.otherParams; try { chrome.storage.local.get(["browserInfo", "platform", "installTime", "userPhoneNum", "firstSendTime", "uuid", "version", "language"], (function(t) { if (!a) { a = !0; try { let d = void 0 !== t.browserInfo ? t.browserInfo : "", i = d ? d.userAgent : "", s = d ? d.browser : "", l = void 0 !== t.platform ? t.platform : "", c = void 0 !== t.installTime ? t.installTime : "", h = t.userPhoneNum, f = void 0 !== t.firstSendTime ? t.firstSendTime : "", $ = void 0 !== t.uuid ? t.uuid : "", p = void 0 !== t.version ? t.version : "", m = void 0 !== t.language ? t.language : "", y = chrome.runtime.getManifest().version, b = void 0 === e ? null : o["a"][e], v = { event_source: 9, event_type: e, event_type_desc: b, event_time: Math.round(new Date / 1e3), install_time: c, s_first_send_time: f, platform: l, user_agent: i, browser_type: s, uuid: $, version: p, version_current: y, language: m }; if (void 0 !== h) { let t = r("a689"), e = t("+" + h).getRegionCode(), n = new t(h.toString(), e), d = n.getRegionCode() ? n.getRegionCode() : "", i = A(d) ? A(d) : ""; v.s_user_phone_num = h, v.s_country_code = i, chrome.storage.local.set({ phoneNum: h, countryCode: i }) } else v.s_user_phone_num = "", v.s_country_code = ""; if (u = { event_source: v.event_source, install_time: v.install_time, s_first_send_time: v.s_first_send_time, platform: v.platform, user_agent: v.user_agent, browser_type: v.browser_type, uuid: v.uuid, version: v.version, version_current: v.version_current, language: v.language, s_user_phone_num: v.s_user_phone_num, s_country_code: v.s_country_code }, n) for (let t in n) v[t] = n[t]; g.send(v) } catch (d) { c("[log-util] dealLog failed", d) } finally { a = !1 } } })) } catch (d) { a = !1, c("[log-util] dealLog failed", d) }}- extension-us.us-west-1.log.aliyuncs.com
Receives the phone number, derived country code, user agent, and install UUID via Alibaba SLS project 'extension-us', logstore 'scrm-web-sender-juzhen', on every logged event.
Uninstall URL includes the WhatsApp number
After WhatsApp Web loads, the extension reads and saves the phone number.
The background script sets Chrome's uninstall URL for wawebsender.com with that number in utm_source, plus install time, country, UUID.
No navigation captured.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You use WhatsApp Web while the extension is active.
The content script runs after the page loads and reads the WhatsApp account identifier from browser storage.
The extension prepares an uninstall URL that includes your WhatsApp number.
Chrome opens that URL if the extension is removed after the number has been stored.
- WhatsApp number+15551234567 (illustrative)
This directly ties the uninstall event to your WhatsApp account number.
- Browser profile UUID8f01c3d9-4bc0-42e4-8d49-e4d3d820a315
This can link the uninstall event back to the same browser profile across extension events.
- Install time1719849600000
This records when the extension was first installed on the browser profile.
- Country valueUnited States (illustrative)
This adds regional context derived from the phone number.
- Extension version1.3.0
This identifies which installed version generated the uninstall URL.
- Connection IP address198.51.100.24 (illustrative)
The receiving server can associate the uninstall request with the network connection that made it.
No request body is used; this is the URL shape the extension registers for Chrome to open on uninstall.
The content script collects the number, then the background script writes it into the uninstall URL
Content script extracts `last-wid` and sends `userPhoneNum`
deobfuscated/js/inject/content-script.js:86751-86764function Ht() { try { let e, t = window.localStorage.getItem("extensionCrm"); t && chrome.storage.local.set({ extensionCrm: !0 }).then(e => {}), window.localStorage.getItem("last-wid") ? (e = window.localStorage.getItem("last-wid"), ht = e.split("@")[0].substring(1)) : (e = window.localStorage.getItem("last-wid-md"), ht = e.split(":")[0].substring(1)) } catch (e) {} return chrome.storage.local.set({ userPhoneNum: ht, loadTimes: 1 }).then(e => {}), b(Y["e"], { userPhoneNum: ht }, "background"), ht}Background handler builds the uninstall URL with `utm_source`
deobfuscated/js/background.js:14527-14543_(R["e"], async ({ data: t}) => { let e = r("a689"), d = e("+" + t.userPhoneNum).getRegionCode(), n = new e(t.userPhoneNum.toString(), d), i = n.getRegionCode() ? n.getRegionCode() : "", o = Object(E["c"])(i) ? Object(E["c"])(i) : "", a = t.userPhoneNum; chrome.storage.local.get(["installTime", "uuid", "version"], (function(t) { let e = void 0 !== t.installTime ? t.installTime : "", r = void 0 !== t.uuid ? t.uuid : "", d = void 0 !== t.version ? t.version : "", n = chrome.runtime.getManifest().version, i = "https://wawebsender.com/waplus-sender-uninstall/?utm_source=" + a + "&install_time=" + e + "&country_name=" + o + "&uuid=" + r + "&version=" + d + "&version_current=" + n + "&extension_name=" + s["a"]; s["c"] || chrome.runtime.setUninstallURL(i) }))})- wawebsender.com
Receives the uninstall URL containing the phone-number query parameter and install metadata.
AI rewrite sends WhatsApp draft text to remote services
Enabling AI Rewrite sends the draft text and rewrite count to wawebsender.com/api/ai/rewrite, with your stored WhatsApp number in a header, and logs the call to Alibaba Cloud as event 901402.
Not captured live; per shipped code.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You ask the extension to rewrite a WhatsApp message with AI.
The UI exposes this as `AI Rewrite` and says it generates personalized copies after enabling.
The extension sends the draft message text to a rewrite API and records the API input and output in its log pipeline.
The logging code includes the request object, the response object, timing fields, and stored user identifiers.
- WhatsApp draft textCan we move the meeting to 3 PM? (illustrative)
This is the message content you are preparing before sending it in WhatsApp.
- Rewrite count25
This tells the service how many rewritten message variants to generate.
- WhatsApp number+15551234567 (illustrative)
This ties the rewrite request to your WhatsApp account number.
- Rewrite responseCould we shift our meeting to 3 PM? (illustrative)
The rewritten message variants can also be passed into the extension's logging path.
- API URL in loghttps://wawebsender.com/api/ai/rewrite
This identifies which remote API call the logged input and output came from.
The code expects a JSON response whose `data.rewrites` array is displayed in the AI Rewrite preview.
- Content-Type
- application/json
- Whatsapp-Number
- +15551234567 (illustrative)
The bundled log client posts queued records to Alibaba Cloud Simple Log Service.
- Content-Type
- application/x-protobuf
- x-log-apiversion
- 0.6.0
AI Rewrite UI, rewrite request, API log event, and Alibaba Cloud transport
AI Rewrite switch triggers generation
deobfuscated/js/popup.js:3930-3934aiRewriteEnabled: { immediate: !1, handler(e) { e ? this.aiRewriteResults && 0 !== this.aiRewriteResults.length || this.handleAiRewriteGenerate(1) : (this.aiRewriteLoading = !1, this.aiRewriteError = !1) }}The generation method reads draft text and calls the rewrite helper
deobfuscated/js/popup.js:3944-3995async handleAiRewriteGenerate(e = 1) { if (!this.aiRewriteLoading) { this.$sendLog(909100, { c_message: "AI重写开始生成", c_type: e }), this.aiRewriteLoading = !0, this.aiRewritePreviewExpanded = !1, this.aiRewriteResults = [], this.aiRewriteEditingId = null, this.aiRewriteEditingDraft = "", this.aiRewriteError = !1; try { var t, a, o; const e = String(null !== (t = this.messageText) && void 0 !== t ? t : "").trim(), i = null !== (a = this.aiRewriteTotal) && void 0 !== a ? a : 0, n = Number(i); if (!e) return this.aiRewriteEnabled = !1, chrome.storage.local.set({ aiRewriteEnabled: !1 }), this.$message({ type: "warning", message: this.$t("ai_rewrite_validate_message_empty_t") }), void this.$sendLog(909101, { c_type: 1, c_message: "输入内容为空" }); if (!Number.isFinite(n) || n <= 0) return this.aiRewriteEnabled = !1, chrome.storage.local.set({ aiRewriteEnabled: !1 }), this.$message({ type: "warning", message: this.$t("ai_rewrite_validate_total_invalid_t") }), void this.$sendLog(909101, { c_type: 1, c_message: "发送的数量为空" }); let r = await ne(e, n, "gpt-4o"), l = (null === r || void 0 === r || null === (o = r.data) || void 0 === o ? void 0 : o.rewrites) || []; if (!Array.isArray(l) || 0 === l.length) throw new Error("AI 返回内容为空"); if (Array.isArray(l) && l.length > 0 && l.length < n) { const e = []; for (let t = 0; t < n; t++) e.push(l[t % l.length]); l = e } this.aiRewriteResults = (l || []).slice(0, n).map((e, t) => ({ id: t + 1, text: String(e || "") })); try { chrome.storage.local.set({ aiRewriteEnabled: this.aiRewriteEnabled, aiRewriteResults: this.aiRewriteResults }) } catch (s) {} } catch (i) { this.aiRewriteError = !0 } finally { this.aiRewriteLoading = !1 } }}Rewrite helper reads the stored WhatsApp number
deobfuscated/js/popup.js:3720-3739const ne = async (e, t = 300, a = "gpt-4o") => { const o = await chrome.storage.local.get(["whatsappNumber"]), s = Date.now(), i = await Object(ie["a"])(e, t, o.whatsappNumber), n = Date.now() - s; if (!i) throw Object(F["c"])(909101, { c_type: 3, c_message: "无返回数据", c_duration_ms: n }), new Error("AI 返回内容为空"); if (1e5 !== i.code && "100000" !== i.code) throw Object(F["c"])(909101, { c_type: 3, c_message: JSON.stringify(i), c_duration_ms: n }), new Error(i.message); return Object(F["c"])(909101, { c_type: 2, c_message: JSON.stringify(i), c_duration_ms: n }), i};Rewrite POST sends draft text and count, with WhatsApp number in a header
deobfuscated/js/popup.js:10683-10717async function g(e, t, a) { const n = Date.now(), r = o["a"] + "api/ai/rewrite", l = { text: e, maxNum: t, whatsappNumber: a }; try { const o = await fetch(r, { method: "POST", headers: { "Content-Type": "application/json", "Whatsapp-Number": a }, body: JSON.stringify({ text: e, maxNum: t }) }); 200 !== o.status && Object(s["a"])({ eventType: 900014, otherParams: { url: o.url, status: o.status } }); const c = await o.json(); return Object(i["a"])(r, l, c, n, !0), c } catch (c) { throw Object(i["a"])(r, l, { error: (null === c || void 0 === c ? void 0 : c.message) || String(c) }, n, !1), c }}API logger records URL, input, output, and duration as event 901402
deobfuscated/js/popup.js:14584-14609const s = 901402;function i(e) { try { if (void 0 === e) return ""; const t = "string" === typeof e ? e : JSON.stringify(e); return t.length > 12e3 ? t.slice(0, 12e3) + "...[truncated]" : t } catch (t) { return String(e) }}function n(e, t, a, n, r) { const l = Date.now() - n; Object(o["a"])({ eventType: s, otherParams: { c_type: r ? 1 : 2, s_api_url: e, s_api_input: i(t), s_api_output: i(a), s_request_time_ms: n, s_request_duration_ms: l } })}Log transport posts queued records to Alibaba Cloud SLS
deobfuscated/js/inject/content-script.js:17231-17264logger: async function(e = !1) { var t = !1 !== e && e.length > 0 ? e : []; if (0 !== t.length) { var n = "https://" + this.project + "." + this.host + "/logstores/" + this.logstore + "/track"; try { var r = JSON.stringify({ __logs__: t }); let e = new Headers({ "x-log-apiversion": "0.6.0", "x-log-bodyrawsize": r.length }); if (this.compress) { e.append("x-log-compresstype", "lz4"); var i = c.from(r), o = d.encodeBound(r.length), a = c.alloc(o), s = d.encodeBlock(i, a); a = a.slice(0, s), fetch(n, { method: "POST", headers: e, body: a }).then(e => e) } else { let t = new Blob([r], { type: "application/x-protobuf" }); e.append("Content-Type", "application/x-protobuf"), fetch(n, { method: "POST", headers: e, body: t }).then(e => e) } } catch (l) {} }}- wawebsender.com
Receives the AI Rewrite API request containing the draft text and rewrite count.
- us-west-1.log.aliyuncs.com
Alibaba Cloud Simple Log Service endpoint that receives queued extension log records, including the rewrite API input and output fields.
+2 more findings not shown