Is WAScheduler - WA Message Scheduler safe?
WADeck reads your WhatsApp phone number and sends URLs from private conversations to third-party link-preview servers.
The extension reads the signed-in user's phone number from WhatsApp Web's localStorage and transmits it, along with a persistent install ID and browser fingerprint, to an Alibaba Cloud logging endpoint. When a message containing a URL is sent, the extension forwards that URL to one of three external link-preview proxy servers — unaffiliated with WhatsApp — to fetch page metadata.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
WhatsApp message URLs sent to preview proxy servers
When a WhatsApp Web message contains a URL, WAScheduler sends it to an external proxy: a GET to wajsapi.titanchat.com.br/v1/link-preview/fetch-data.png, with the outgoing message URL in the url query parameter.
What actually happens
You send a WhatsApp Web message that contains a URL.
The extension requests link-preview metadata from an external proxy endpoint.
The URL from the message is placed in the request's query string.
Captured request
JSON preview metadata containing title, description, image, and URL fields.
Field sent to the link-preview proxy
- Message URLhttps://example.com/article (illustrative)
This is the web address typed into your WhatsApp message. It can reveal the site or content being discussed in that conversation.
Configured link-preview proxy hosts
- wajsapi.titanchat.com.br
Receives the message URL in a link-preview fetch request; this was the host named in the confirmed verification.
- cobrancas.uppermesh.com.br
- wppc-linkpreview.cloudtrix.com.br
Configured as an alternate link-preview proxy host in the shipped extension code.
The code that does this
The injected WhatsApp helper sends the message URL to a preview proxy
The content script injects the page helper
content.bundle.jscase 16: i["default"].prototype.$injectEl = this.appLoc, this.appendScriptTag(e.runtime.getURL("qpqp.js")), this.appendScriptTag(e.runtime.getURL("inject.bundle.js")), this.appendTagTo(this.appLoc, O.toobarId), new i["default"]({ i18n: b["a"], store: this.store, render: function(e) { return e(s["a"]) } }).$mount("#" + O.toobarId)Outgoing message text is scanned for the first web link
inject.bundle.jst.prepareLinkPreview = function() { var e = _asyncToGenerator(_regeneratorRuntime.mark((function e(t, r) { var n, o, i, a; return _regeneratorRuntime.wrap((function(e) { while (1) switch (e.prev = e.next) { case 0: if (r.linkPreview) { e.next = 2; break } return e.abrupt("return", t); case 2: if (!r.linkPreview) { e.next = 16; break } if (n = "object" == _typeof(r.linkPreview) ? r.linkPreview : {}, o = "chat" === t.type ? t.body : "", !o) { e.next = 16; break } if (e.prev = 5, i = (0, f.findFirstWebLink)(o), !i) { e.next = 12; break } return e.next = 10, (0, f.fetchLinkPreview)(i); case 10: a = e.sent, (null == a ? void 0 : a.data) && (r.linkPreview = Object.assign(Object.assign({}, a.data), n)); case 12: e.next = 16; break; case 14: e.prev = 14, e.t0 = e["catch"](5); case 16: return "object" == _typeof(r.linkPreview) && (t.subtype = "url", t = Object.assign(Object.assign({}, t), r.linkPreview)), e.abrupt("return", t); case 18: case "end": return e.stop() } }), e, null, [ [5, 14] ]) }))); return function(t, r) { return e.apply(this, arguments) }}();The proxy request is built with the message URL in the query string
inject.bundle.jst.fetchRemoteLinkPreviewData = function() { var e = _asyncToGenerator(_regeneratorRuntime.mark((function e(t) { var r, n, o, i, a, u, s; return _regeneratorRuntime.wrap((function(e) { while (1) switch (e.prev = e.next) { case 0: if (!p[t]) { e.next = 2; break } return e.abrupt("return", (l("Link preview found in the cache", t), p[t])); case 2: r = new TextDecoder, n = f.length - 1; case 4: if (!(n >= 0)) { e.next = 21; break } return o = f[n], l("Fetching link preview using ".concat(o), t), i = "".concat(o, "/v1/link-preview/fetch-data.png?url=") + encodeURI(t), e.next = 10, (0, c.fetchDataFromPNG)(i).then((function(e) { return r.decode(e) })).then((function(e) { return JSON.parse(e) })).catch((function() { return null })); case 10: if (a = e.sent, null !== a && ("title" in a || "status" in a)) { e.next = 14; break } return l("The server ".concat(o, " is unavailable for link preview")), f.splice(n, 1), e.abrupt("continue", 18); case 14: if (a.title || 200 === a.status) { e.next = 16; break } return e.abrupt("continue", 18); case 16: return u = /^video/.test(a.mediaType), s = { title: a.title, description: a.description, canonicalUrl: a.url, matchedText: t, richPreviewType: u ? 1 : 0, doNotPlayInline: !u, imageUrl: a.image }, e.abrupt("return", (p[t] = s, setTimeout((function() { delete p[t] }), 3e5), s)); case 18: n--, e.next = 4; break; case 21: return e.abrupt("return", null); case 22: case "end": return e.stop() } }), e) }))); return function(t) { return e.apply(this, arguments) }}()The configured server list is randomized before use
inject.bundle.jsvar o = n(r(17833)), i = r(91491), a = r(14647), u = r(52757), s = r(97016), c = r(24298), l = (0, o.default)("WA-JS:link-preview"), f = i.config.linkPreviewApiServers || ["https://cobrancas.uppermesh.com.br:8000", "https://wajsapi.titanchat.com.br", "https://wppc-linkpreview.cloudtrix.com.br"], d = 140, p = {}, v = {};! function(e) { for (var t = e.length - 1; t > 0; t--) { var r = Math.floor(Math.random() * (t + 1)), n = [e[r], e[t]]; e[t] = n[0], e[r] = n[1] }}(f)Where it sends data
Destinations our analysis observed WAScheduler - WA Message Scheduler 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
WAScheduler - WA Message Scheduler 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
WAScheduler - WA Message Scheduler 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
WAScheduler - WA Message Scheduler sends data to cobrancas.uppermesh.com.br. A widely used service: 31 other extensions we have analyzed send data here.
- extension-us.us-west-1.log.aliyuncs.comwidely used
WAScheduler - WA Message Scheduler sends data to extension-us.us-west-1.log.aliyuncs.com. A widely used service: 28 other extensions we have analyzed send data here.
