Is TG Sender - TG bulk message send and invite safe?

High risk

Telegram Sender is high risk. TG Sender records workflow details via its bundled Aliyun logging helper. Dynamic analysis confirmed Start Inviting reaches the invite logger with the group link and username list; source also passes message text and scraped profile URLs.

adtargetinghellov3.5.3Chrome Web Store
72Risk
Who publishes it

adtargetinghello - 3 other listings from the same operator, 1 of them carrying a finding

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

Publisher
adtargetinghello

Same store account

3 other listings published from this account, 207k+ users between them. 1 of them carries a finding.

Shared hosts - 2 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.

waputech.com
Also called by 1 other listing: TelePlus - Multi-Accounts Sender
teleplus.io
Also called by 5 other listings, including TelePlus - Multi-Accounts Sender

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

Publishers can request a review.

Findings

Telegram workflow data is posted to an Aliyun logstore

TG Sender records workflow details via its bundled Aliyun logging helper.

Dynamic analysis confirmed Start Inviting reaches the invite logger with the group link and username list; source also passes message text and scraped profile URLs.

Severity
High unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You start a Telegram automation workflow in the extension.

The confirmed path is the Start Inviting flow; the same logging helper is also called from bulk messaging and group scraping code paths.

The extension did this

The extension sends the workflow fields and extension metadata to an Aliyun logging endpoint.

Dynamic analysis observed the API invite path reaching the logger with a group link and comma-separated username list.

Fields the code passes into the Aliyun log record
  • Message text
    message: Follow up after the webinar today (illustrative)

    Shows the content you entered for an automated Telegram message.

  • Recipient usernames
    usernames: @alice_example,@team_updates (illustrative)

    Shows which Telegram accounts or phone-style entries you targeted in the workflow.

  • Group link
    group_link: https://t.me/example_group (illustrative)

    Identifies the Telegram group or channel you selected for the invite workflow.

  • Scraped profile URL
    user: https://web.telegram.org/k/#123456789 (illustrative)

    Identifies a Telegram profile URL returned by the group-scraping workflow.

  • Extension metadata
    uuid: 8f4d2c91-0b62-4f5a-9b7c-2e1d03a4c5f6 (illustrative)

    Links separate Telegram workflow records back to the same browser installation.

Captured request
POSThttps://extension-us.us-west-1.log.aliyuncs.com/logstores/scrm-tg-sender/track

Confirmed during dynamic analysis; request body was not recorded in the verification evidence.

The code that does this

The logger builds the Aliyun payload and merges workflow fields

Readable version

Shared Aliyun log helper

background.js
function sendLog(eventType, extraFields) {  const client = new AliyunLogger({    host: "us-west-1.log.aliyuncs.com",    project: "extension-us",    time: 0.05,    count: 1,    logstore: "scrm-tg-sender"  });  chrome.storage.local.get(null, (storage) => {    const browserInfo = storage.browserInfo !== undefined ? storage.browserInfo : "";    const payload = {      event_source: 1,      event_type: eventType,      event_time: Math.round(Date.now() / 1000),      install_time: storage.installTime !== undefined ? storage.installTime : "",      platform: browserInfo ? browserInfo.platform : "",      user_agent: browserInfo ? browserInfo.userAgent : "",      browser_type: browserInfo ? browserInfo.browser : "",      uuid: storage.uuid !== undefined ? storage.uuid : "",      version: chrome.runtime.getManifest().version,      language: browserInfo ? browserInfo.language : "",      plan_code: storage.plan_code !== undefined ? storage.plan_code : ""    };    if (extraFields) {      for (const key in extraFields) {        payload[key] = extraFields[key];      }    }    client.send(payload);  });}

Bulk messaging passes message text and usernames

background.js
async function startBulkMessage() {  running = true;  const recipients = settings["message-usernames"] !== undefined    ? settings["message-usernames"].trim().split("\n").filter((value) => value.trim().length > 1)    : [];  const usernameRecipients = [];  const phoneRecipients = [];  for (let index = 0; index < recipients.length; index++) {    if (recipients[index] !== "") {      if (recipients[index].match(/[a-zA-Z@]+/g) !== null) {        usernameRecipients.push(recipients[index]);      } else {        const normalized = recipients[index].replace(/ /g, "").replace(/-/g, "");        recipients[index] = normalized;      }    }  }  if (usernameRecipients.length > phoneRecipients.length) {    sendLog(110003, {      message: settings["message"],      usernames: recipients.join(","),      message_type: "username"    });    chrome.tabs.sendMessage(activeTabId, { type: "message_by_username", data: recipients });  } else {    sendLog(110003, {      message: settings["message"],      usernames: recipients.join(","),      message_type: "phone"    });    chrome.tabs.sendMessage(activeTabId, { type: "add_contact", data: recipients, index: 0 });  }}

API invite validation starts the invite workflow

popup.js
function startApiInvite(skipTip) {  if (this.version_low) {    sendLog(110112, { error: "version too low" });    return;  }  if (this.limit["invite"] <= 0) {    this.upgrade_content = this.plan_code === "free" ? this.invite_tip_1 : this.invite_tip_3;    this.upgradeDialogVisible = true;    sendLog(110112, { error: "limited" });    return;  }  if (this.login_status !== 1) {    this.tip_content = this.api_scrape_tip_2;    this.tipDialogVisible = true;    sendLog(110112, { error: "unlogin" });    return;  }  if (!this.api_invite_group_link) {    this.tip_content = this.invite_tip_6;    this.tipDialogVisible = true;    sendLog(110112, { error: "no group" });    return;  }  if (/\/t.me\//.test(this.api_invite_group_link) === false) {    this.tip_content = this.invite_tip_8;    this.tipDialogVisible = true;    return;  }  const usernameText = this.invite_usernames.replace(/\n/g, ",");  if (usernameText === "") {    this.tip_content = this.invite_tip_2;    this.tipDialogVisible = true;    sendLog(110112, { error: "no usernames" });    return;  }  if (usernameText.split(",").length > this.limit["invite"]) {    this.tip_content = this.invite_tip_4.replace("%", this.limit["invite"]);    this.tipDialogVisible = true;    return;  }  if (this.notShowInviteTip || skipTip) {    const usernames = usernameText.split(",");    this.api_invite_progress = true;    this.before_tab = this.current_tab;    this.current_tab = "progress-tab";    this.apiInvite(usernames, this.api_invite_group_link, 0);  } else {    this.inviteDialogVisible = true;  }}

API invite logs group_link and username_list

popup.js
async function apiInvite(usernames, groupLink, startIndex) {  sendLog(110112, {    group_link: groupLink,    username_list: usernames.join(",")  });  await this.a({ progress: { current: startIndex, total: usernames.length } });  this.progress_interval = setInterval(this.n, 500);  try {    await this.client.connect();  } catch (error) {    console.log(error);    this.tip_content = this.connect_tip;    this.tipDialogVisible = true;    this.api_invite_progress = false;    sendLog(110113, { group_link: groupLink, error: this.tip_content + error.message });    return;  }  let channel = groupLink;  if (/t.me\/\+([\w-]{16})/.test(groupLink)) {    const channelInput = await this.getChannelInput(groupLink.match(/t.me\/\+([\w-]{16})/)[1]);    if (channelInput.error !== undefined) {      this.current_tab = this.before_tab;      sendLog(110113, { group_link: groupLink, error: channelInput.error });      return;    }    channel = channelInput.channelInput;  }  const result = { success: 0, total: usernames.length, await: usernames.length };  for (let index = startIndex; index < usernames.length && this.api_invite_progress; index++) {    const delaySeconds = Math.max(Math.floor(Math.random() * (this.invites_wait_max - this.invites_wait_min) + this.invites_wait_min), 10);    const username = usernames[index];    try {      await this.client.connect();      if (channel.accessHash === undefined && channel.channelId !== undefined) {        await this.client.invoke(new Api.messages.AddChatUser({ chatId: channel.channelId, userId: username }));      } else {        await this.client.invoke(new Api.channels.InviteToChannel({ channel, users: [username] }));      }      await this.client.disconnect();      await this.a({ progress: { current: index + 1, total: usernames.length } });    } catch (error) {      console.log(error);      await this.a({ progress: { current: index + 1, total: usernames.length } });      this.tip_content = error.message;      sendLog(110113, { group_link: groupLink, error: this.tip_content + error.message });      await this.client.disconnect();      break;    }    result.success += 1;    result.await -= 1;    await this.a({ api_invite_result: result });    await this.wait(delaySeconds);  }  await this.a({ progress: { current: usernames.length, total: usernames.length } });  this.api_invite_progress = false;  await this.a({ api_invite_result: null });  sendLog(110113, { group_link: groupLink, result });  this.setLimit("invite", result.success);}

Group scraping logs each scraped profile URL

content-script.js
async function scrapeGroup(groupLink, filter) {  const eventType = 110007;  if (groupLink && window.location.href !== groupLink) {    groupLink += "?tgsendertype=1";    for (const key in filter) {      groupLink += "&" + key + "=" + filter[key];    }    window.open(groupLink, "_blank");    return;  }  const settings = await readStorage("settings");  const planCode = await readStorage("plan_code");  let limit = Number.isNaN(parseInt(settings["limit-group-scraper"])) ? 50 : parseInt(settings["limit-group-scraper"]);  const lastSeen = filter["last_seen"];  const removeAdmin = filter["remove_admin"];  limit = planCode === "basic" || planCode === "pro" ? limit : Math.min(limit, maxFreeScrapeCount);  await writeStorage({ progress: { current: 0, total: limit } });  const selectors = await getSelectors("scrape");  let groupId = "";  let groupMembers = "";  groupLink = window.location.href;  let title = null;  let titleAttempts = 0;  while (titleAttempts < 15 && title == null) {    await wait(1);    title = document.querySelector(selectors["group_title"]);    const broadcastButton = document.querySelector(selectors["channel_broadcast_button"]);    if ((title && !title.getAttribute("data-peer-id").startsWith("-")) || broadcastButton != null) {      title = null;      break;    }    titleAttempts++;  }  if (title == null) {    chrome.runtime.sendMessage({ message: "log", eventType, otherParams: { error: "group info not found", group_link: groupLink } });    chrome.runtime.sendMessage({ message: "complete", data: "scrape" });    running = false;    return;  }  groupId = title.textContent;  title.click();  await wait(1);  const memberRows = [...document.querySelectorAll(selectors["group_list"])];  if (memberRows.length === 0) {    chrome.runtime.sendMessage({ message: "showtip", data: "Group members not loaded, Please reload webpage." });    chrome.runtime.sendMessage({ message: "complete", data: "scrape" });    chrome.runtime.sendMessage({ message: "log", eventType, otherParams: { error: "group members not found", group_link: groupLink } });    running = false;    return;  }  let peerIds = memberRows.map((row) => row.getAttribute("data-peer-id"));  const groupLinks = [...document.querySelectorAll(selectors["group_link"])].filter((node) => /^http.*?$/.test(node.textContent));  if (groupLinks.length > 0) {    groupId = groupLinks[groupLinks.length - 1].textContent;  }  const collectedUsernames = [];  let scrapedCount = 0;  for (let index = 0; running && index < peerIds.length && scrapedCount < limit; index++) {    const userRecord = [];    document.querySelector(selectors["group_title"]).click();    const row = document.querySelector(selectors["user_div"].replace("%user_id%", peerIds[index]));    if (row == null) {      window.location.href = groupLink;      continue;    }    row.click();    await wait(1.5);    const selfLink = document.querySelector(selectors["user_self"]);    if (selfLink != null) {      userRecord[0] = window.location.href;    }    const usernames = [...document.querySelectorAll(selectors["username"])].filter((node) => /^[a-zA-Z0-9_]{1,}$/.test(node.textContent));    userRecord[1] = usernames.length === 0 ? "" : "@" + usernames[0].textContent;    const returnButton = document.querySelector(selectors["group_return"]);    if (returnButton == null) {      window.location.href = groupLink;      continue;    }    returnButton.click();    const username = userRecord[1];    if (username !== undefined && username !== "bot" && username.length > 0 && username !== "Deleted Account" && !collectedUsernames.includes(username) && running) {      scrapedCount++;      collectedUsernames.push(username);      settings["scraped-usernames"] = collectedUsernames.join("\n");      await writeStorage({ settings, progress: { current: scrapedCount, total: limit }, useLog: await shouldUseLog() });    }    if (userRecord[0]) {      chrome.runtime.sendMessage({ message: "log", eventType: 110005, otherParams: { user: userRecord[0] } });    }  }}
External destination receiving the log record
    • extension-us.us-west-1.log.aliyuncs.com

    Aliyun log service endpoint for the extension-us project and scrm-tg-sender logstore; receives workflow fields merged by the extension logger.

Updated 30 September 2026kchbblidjcniipdkjlbjjakgdlbfnhgh