Is Rocket Sender - Bulk WA Messages & Automation safe?

Medium risk

Rocket Sender automates WhatsApp messaging and decides where to activate from a class-name list fetched from its remote config server.

The background script downloads a configuration file from cdn2.waplus.io and stores it locally; the content script (declared on all sites) injects the privileged WhatsApp automation bundle wherever a page matches the remotely supplied CSS class list, so the vendor can change the targeted sites by editing remote config. Background telemetry posts the user's logged-in WhatsApp phone number, a derived country code, install UUID, install time and browser details to an Alibaba Cloud logstore on tracked events. When a user shares a link in WhatsApp, the inject script also forwards the full shared URL to one of three third-party link-preview servers to generate the preview.

rocket-senderv1.6.3Chrome Web Store
49Risk
Who publishes it

rocket-sender - no other listings under this identity, 1 shared hostname

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

Publisher
rocket-sender

Shared hosts - 1 hostname

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.

rocketsender.io
Also called by 6 other listings

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

Publishers can request a review.

Findings

WhatsApp link previews send shared URLs to preview servers

MediumunwantedCWE-200Dynamic sandbox

When you send a WhatsApp message with a link, Rocket Sender replaces the preview generator, sending the full URL to one of three servers: cobrancas.uppermesh.com.br, wajsapi.titanchat.com.br, wppc-linkpreview.cloudtrix.com.br.

What actually happens

You did this

You compose or send a WhatsApp message that includes a link.

The preview path is reached only when WhatsApp Web can generate a link preview inside an active chat.

The extension did this

The extension builds a preview request that includes the full link.

The link is appended as the url query parameter before the request is sent to a configured preview server.

Fields placed into the preview request

  • Shared link
    https://example.com/secret-path (illustrative test URL from the verification recipe)

    This can reveal the exact page, document, invite, or other destination you are sending in a WhatsApp chat.

  • Request parameter
    url=https://example.com/secret-path (illustrative test URL from the verification recipe)

    The link is sent in the address bar of the preview request rather than kept only inside WhatsApp Web.

  • Preview destination
    wajsapi.titanchat.com.br

    A server selected by the extension receives the shared link so it can return preview metadata.

Captured request

GEThttps://wajsapi.titanchat.com.br/v1/link-preview/fetch-data.png?url=https://example.com/secret-path

No live response was captured during traffic testing because WhatsApp Web did not reach a logged-in chat session.

The code that does this

The shipped code wraps WhatsApp's preview generator and builds the preview-server URL

Readable version

Wrapper that sends the shared URL into the remote preview path

deobfuscated/inject.js
u.onFullReady((() => {  (0, l.wrapModuleFunction)(d.getABPropConfigValue, ((e, ...t) => {    const [r] = t;    return "link_preview_wait_time" === r ? 7 : e(...t)  })), (0, l.wrapModuleFunction)(d.genMinimalLinkPreview, (async (e, ...t) => {    const [r] = t, n = "string" == typeof r ? r : r.url;    return new Promise((async r => {      try {        const e = await (0, c.fetchRemoteLinkPreviewData)(n);        if (!e) throw new Error(`preview not found for ${n}`);        const {          imageUrl: t        } = e, o = s(e, ["imageUrl"]);        let i = {};        t && (i = await (0, c.generateThumbnailLinkPreviewData)(t).catch((() => null))), r({          url: n,          data: Object.assign(Object.assign({}, o), i)        })      } catch (n) {        r(await e(...t))      }    }))  }))}))

Request builder and configured preview servers

deobfuscated/inject.js
t.fetchRemoteLinkPreviewData = async function(e) {  if (p[e]) return l("Link preview found in the cache", e), p[e];  const t = new TextDecoder;  for (let r = d.length - 1; r >= 0; r--) {    const n = d[r];    l(`Fetching link preview using ${n}`, e);    const o = `${n}/v1/link-preview/fetch-data.png?url=` + encodeURI(e),      i = await (0, c.fetchDataFromPNG)(o).then((e => t.decode(e))).then((e => JSON.parse(e))).catch((() => null));    if (null === i || !("title" in i) && !("status" in i)) {      l(`The server ${n} is unavailable for link preview`), d.splice(r, 1);      continue    }    if (!i.title && 200 !== i.status) continue;    const a = /^video/.test(i.mediaType),      s = {        title: i.title,        description: i.description,        canonicalUrl: i.url,        matchedText: e,        richPreviewType: a ? 1 : 0,        doNotPlayInline: !a,        imageUrl: i.image      };    return p[e] = s, setTimeout((() => {      delete p[e]    }), 3e5), s  }  return null};const d = i.config.linkPreviewApiServers || ["https://cobrancas.uppermesh.com.br:8000", "https://wajsapi.titanchat.com.br", "https://wppc-linkpreview.cloudtrix.com.br"];! function(e) {  for (let t = e.length - 1; t > 0; t--) {    const r = Math.floor(Math.random() * (t + 1));    [e[t], e[r]] = [e[r], e[t]]  }}(d)

PNG response helper that performs the image GET

deobfuscated/inject.js
t.fetchDataFromPNG = function(e) {  return new Promise(((t, r) => {    const n = new Image;    n.crossOrigin = "anonymous", n.src = e, n.onerror = r, n.onload = function() {      const e = document.createElement("canvas"),        r = e.getContext("2d");      e.width = n.naturalWidth, e.height = n.naturalHeight, r.drawImage(n, 0, 0);      const o = r.getImageData(0, 0, e.width, e.height).data,        i = new Uint8Array(Math.floor(3 * o.length / 4));      for (let t = 0, n = 0; t < o.length; t += 4) i[n++] = o[t], i[n++] = o[t + 1], i[n++] = o[t + 2];      let a = BigInt(0);      for (let t = 0; t < 8; t++) a = (a << BigInt(8)) + BigInt(i[t]);      const s = Number(a);      t(i.subarray(8, 8 + s))    }  }))}

Preview servers that can receive the shared URL

    • cobrancas.uppermesh.com.br
    • wajsapi.titanchat.com.br
    • wppc-linkpreview.cloudtrix.com.br

    Fallback preview API server from the shipped server list; the code appends /v1/link-preview/fetch-data.png?url= and the shared link.

Remote config picks which sites get the WhatsApp automation bundle

MediumunwantedCWE-829Dynamic sandbox

Rocket Sender's content script runs on every site, checking CSS class names from cdn2.waplus.io (refreshed every 5 min); a matching class injects bundle inject.js.

What actually happens

You did this

You open a website in your browser.

The content script is registered on <all_urls>, so it runs on the page regardless of which site it is.

The extension did this

The extension checks the page against a class-name list it downloaded from the developer's server, and injects its automation bundle if the page matches.

The list is fetched from cdn2.waplus.io and refreshed every five minutes, so the developer controls which pages activate the automation.

The code that does this

The content script gates injection on a remotely supplied class list

Readable version

content.js — readable

content.js
async function maybeInject() {  if (alreadyInjected) return;  const { zbaseConfig } = await chrome.storage.local.get(["zbaseConfig"]);  const { class_list } = zbaseConfig?.data?.config    ?.find(e => e.name === "WAPI Class")?.params || [];  for (const cssClass of class_list) {    if (document.querySelector(`.${cssClass}`)) {   // page DOM matches a remote class      alreadyInjected = true;      await injectScript("/inject.js", "inject");   // add privileged bundle to the page      window.localStorage.setItem("rocketSender", true);      await startAutomation();      break;    }  }  if (!alreadyInjected) setTimeout(maybeInject, 2000); // re-poll the DOM}function injectScript(path, id) {  return new Promise((resolve, reject) => {    const s = document.createElement("script");    s.id = id;    s.src = chrome.runtime.getURL(path);            // inject.js from the extension bundle    s.onload = resolve; s.onerror = reject;    document.head.appendChild(s);  });}

The code that does this

The background service worker fetches and caches the remote class list

Readable version
function refreshConfig() {  chrome.storage.local.get(["zbaseConfig", "configLastFetchTime"], (cached) => {    const now = Date.now();    const TTL = 300000; // 5 minutes    const stale = !cached.zbaseConfig || !cached.configLastFetchTime               || now - cached.configLastFetchTime > TTL;    if (stale) {      fetch("https://cdn2.waplus.io/extensiton/rocket-sender/config/config.json",            { cache: "no-store" })        .then(r => r.json())        .then(cfg => chrome.storage.local.set({          zbaseConfig: cfg,          configLastFetchTime: now,        }));    }  });}

Captured request

GEThttps://cdn2.waplus.io/extensiton/rocket-sender/config/config.json

Observed during dynamic analysis (HTTP 200). The response was stored as chrome.storage.local key zbaseConfig. Its 'WAPI Class' entry carried params.class_list = [x78zum5, xdt5ytf, x5yr21d, _ak9y, _ak8q], the CSS classes that gate where inject.js activates.

Headers
Cache-Control
no-store

When this fires

Every 5 minutes

The background service worker re-fetches the remote class list whenever the cached copy is older than five minutes, so the set of pages that activate the automation can change roughly every five minutes without an extension update.

What dynamic analysis observed

During dynamic analysis the background request to cdn2.waplus.io returned the class list, and inject.js was injected only on web.whatsapp.com, whose DOM contains those classes. After navigating to example.com and Wikipedia (which contain none of the listed classes), no further injection occurred. This confirms the activation is controlled by the remote class list rather than the <all_urls> manifest pattern. The injected code (inject.js) ships inside the extension and depends on WhatsApp Web internals, so the remote config controls targeting, not the code body.

Where it sends data

Destinations our analysis observed Rocket Sender - Bulk WA Messages & Automation contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • wajsapi.titanchat.com.brwidely used

    Rocket Sender - Bulk WA Messages & Automation sends data to wajsapi.titanchat.com.br. A widely used service: 37 other extensions we have analyzed send data here.

  • wppc-linkpreview.cloudtrix.com.brwidely used

    Rocket Sender - Bulk WA Messages & Automation sends data to wppc-linkpreview.cloudtrix.com.br. A widely used service: 35 other extensions we have analyzed send data here.

  • cobrancas.uppermesh.com.brwidely used

    Rocket Sender - Bulk WA Messages & Automation sends data to cobrancas.uppermesh.com.br. A widely used service: 31 other extensions we have analyzed send data here.

  • us-west-1.log.aliyuncs.com

    Rocket Sender - Bulk WA Messages & Automation sends data to us-west-1.log.aliyuncs.com. 12 other extensions we have analyzed send data here.

  • cdn2.waplus.io

    Rocket Sender - Bulk WA Messages & Automation sends data to cdn2.waplus.io. No other extension we have analyzed sends data here.

Updated 30 September 2026hmpoalkkjnapmpfgleamekjhhfenpodc