Is Web for Telegram safe?

Medium risk

Web for Telegram is medium risk. Web for Telegram asks jitt.wwevents.fun for RemoteMessage entries and renders the first MESSAGE string via toastr before DOMPurify applies. Toastr has HTML escaping disabled, so server HTML can enter the Telegram Web page.

WWEventsv7.1Chrome Web Store
47Risk
Who publishes it

wwevents - 17 other listings from the same operator, none carrying a finding

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

Publisher
WWEvents
Declared legal entity
wwevents
Registered address
Persiaran Sukan, Shah Alam, Selangor 40100, MY

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.

pay.oinkandstuff.com
Also called by 2 other listings, including Web for TikTok
pay.wwevents.fun
Also called by 2 other listings, including Web for TikTok

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

Publishers can request a review.

Findings

Remote HTML appears in Telegram Web before sanitization

MediumunwantedCWE-829Dynamic sandbox

Web for Telegram asks jitt.wwevents.fun for RemoteMessage entries and renders the first MESSAGE string via toastr before DOMPurify applies.

What actually happens

You did this

You open Telegram Web while the extension's remote-message flow is enabled.

The content script waits briefly, then asks the service worker for remote message content.

The extension did this

The extension can render the first server-provided message as HTML inside the Telegram Web page.

Later messages use DOMPurify when the next button is clicked, but the first display is passed directly to toastr.info.

Captured request

GEThttps://jitt.wwevents.fun/wwevents-remote/api/v1/RemoteMessage?app=ChromeGen2eric

Captured request

GEThttps://jitt.wwevents.fun/wwevents-remote/api/v1/RemoteMessage?app=ChromeNewsGen2eric

Concrete fields and parameters in the remote-message path

  • Remote message host
    jitt.wwevents.fun

    This is the server your browser contacts before the message is shown in Telegram Web.

  • Application selector
    ChromeGen2eric

    This value tells the server which message set the extension is requesting.

  • Message field
    jsonData[0].data[i].MESSAGE

    This response field is the content that can become part of the Telegram Web page.

  • Toast element
    #remoteMessageToastr .toast-message

    This is where the first message is placed after it is received.

The code that does this

Remote message fetches flow into an unsanitized first toast render

Readable version

Service worker fetches RemoteMessage records

eventPage.js
switch (message.remoteMessage) {        case "remoteMessage":			chrome.storage.sync.get("remoteMessageDate", async function (data) {				//console.log("remoteMessageDate", data['remoteMessageDate']);                var today = new Date();                var serializedToday = JSON.stringify(today);                if (data['remoteMessageDate'] == null) {                    chrome.storage.sync.set({ 'remoteMessageDate': serializedToday }, function () { });					chrome.storage.sync.set({ 'remoteMessageSID': "" }, function () { });					sendResponse({ remoteMessage: "" });                } else {					var savedDate = new Date(JSON.parse(data['remoteMessageDate']));                    var datesDiff = Date.dateDiff('d', savedDate, today);					if (await getObjectFromLocalStorage('showToasterOnStartup1')){						fetch('https://jitt.wwevents.fun/wwevents-remote/api/v1/RemoteMessage?app=ChromeGen2eric')						.then(response => response.json()).then((result) => {															var jsonData = result;								if(jsonData != null && jsonData.length > 0 && jsonData[0].result == "success"){									var responsestring = [];									for (var i = 0; i < jsonData[0].data.length; i++)									{										responsestring.push(jsonData[0].data[i].MESSAGE);									}									chrome.storage.sync.get("remoteMessageSID", function (data) {										var storedId = data['remoteMessageSID'];										if((storedId.toUpperCase() != jsonData[0].data[0].SID.toUpperCase() || jsonData[0].data[0].DURATION.toUpperCase() == "Perm".toUpperCase()) && (datesDiff >= 1)){ //Add datesDiff >= 6											chrome.storage.sync.set({ 'remoteMessageSID': jsonData[0].data[0].SID }, function () { });											sendResponse({ remoteMessage: responsestring });										} else {											sendResponse({ remoteMessage: "" });										}										chrome.storage.local.set({ 'showToasterOnStartup1': false }, function () { });									});								}													}).catch(error => {							console.log(error);						});					}				}			});			return true; //so i can use sendResponse later            break;        default:            break;    }	switch (message.informationNews) {        case "informationNews":			fetch('https://jitt.wwevents.fun/wwevents-remote/api/v1/RemoteMessage?app=ChromeNewsGen2eric')			.then(response => response.json()).then((result) => {					var jsonData = result;					if(jsonData != null && jsonData.length > 0 && jsonData[0].result == "success"){						var responsestring = [];						for (var i = 0; i < jsonData[0].data.length; i++)						{							responsestring.push(jsonData[0].data[i].MESSAGE);						}						sendResponse({ informationNews: responsestring });					}			}).catch(error => {				console.log(error);			});			return true; //so i can use sendResponse later            break;        default:            break;    }

Content script renders the first returned message

myScript.js
/* Show a fixed Toast for remote messages */			setTimeout(function(){				if(toastrSemaphoreOnce){					chrome.runtime.sendMessage({ remoteMessage: "remoteMessage" }, function (response) {						if(response == undefined || Object.keys(response).length == 0) return;						if (response.remoteMessage != "") {							toastrSemaphoreOnce = false;							remoteMessage = response.remoteMessage;							toastr.options = { "positionClass": "toast-top-full-width", "timeOut": "0", "progressBar": "true", "closeButton" : "true", "extendedTimeOut": "0", "tapToDismiss" : false };							var t = toastr.info(remoteMessage[remoteMessageIndex]);							t.attr('id', 'remoteMessageToastr');							$('#remoteMessageToastr').append('<span id="remoteMessageSpan" style="font-weight: 600;opacity: 0.75;top: 0.4em;left: 0.4em;position: absolute;">'+ (remoteMessageIndex + 1) + '/'+ remoteMessage.length +'</span>');							manageRemoteMessage();						}					});				}			}, 2100);

News menu uses the same first-render pattern, while next uses DOMPurify

myScript.js
function clickInformationNews(){			chrome.runtime.sendMessage({ informationNews: "informationNews" }, function (response) {				if(response == undefined || Object.keys(response).length == 0) return;				if (response.informationNews != null) {					remoteMessage = response.informationNews;					toastr.options = { "positionClass": "toast-top-full-width", "timeOut": "0", "progressBar": "true", "closeButton" : "true", "extendedTimeOut": "0", "tapToDismiss" : false };					var t = toastr.info(remoteMessage[remoteMessageIndex]);					t.attr('id', 'remoteMessageToastr');					$('#remoteMessageToastr').append('<span id="remoteMessageSpan" style="font-weight: 600;opacity: 0.75;top: 0.4em;left: 0.4em;position: absolute;">'+ (remoteMessageIndex + 1) + '/'+ remoteMessage.length +'</span>');					manageRemoteMessage();				}			});		}				function manageRemoteMessage(){			$('#remoteMessageToastr .toast-message button').css({'background' : '#E5E5E5', 'color':'black'});			document.querySelector('#remoteMessageToastr #surpriseNextBtn').addEventListener('click', function () { 				remoteMessageIndex++;				if(remoteMessageIndex >= remoteMessage.length) remoteMessageIndex = 0;				$('#remoteMessageToastr .toast-message').html(DOMPurify.sanitize(remoteMessage[remoteMessageIndex], {ADD_ATTR: ['target']}));				$('#remoteMessageSpan').text((remoteMessageIndex + 1) + '/'+ remoteMessage.length);				manageRemoteMessage();			});			document.querySelector('#remoteMessageToastr #surpriseBtn').addEventListener('click', function () { $('#remoteMessageToastr').fadeOut("slow", function () { $('#remoteMessageToastr').remove(); }); });		}

Toastr defaults leave message HTML unescaped

js/toastr/toastr.min.js
function p() {        return {          tapToDismiss: !0,          toastClass: "toast",          containerId: "toast-container",          debug: !1,          showMethod: "fadeIn",          showDuration: 300,          showEasing: "swing",          onShown: void 0,          hideMethod: "fadeOut",          hideDuration: 1e3,          hideEasing: "swing",          onHidden: void 0,          closeMethod: !1,          closeDuration: !1,          closeEasing: !1,          closeOnHover: !0,          extendedTimeOut: 1e3,          iconClasses: {            error: "toast-error",            info: "toast-info",            success: "toast-success",            warning: "toast-warning"          },          iconClass: "toast-info",          positionClass: "toast-top-right",          timeOut: 5e3,          titleClass: "toast-title",          messageClass: "toast-message",          escapeHtml: !1,          target: "body",          closeHtml: '<button type="button">&times;</button>',          closeClass: "toast-close-button",          newestOnTop: !0,          preventDuplicates: !1,          progressBar: !1,          progressClass: "toast-progress",          rtl: !1        }      }function d() {          if (t.message) {            var e = t.message;            E.escapeHtml && (e = o(t.message)), B.append(e).addClass(E.messageClass), I.append(B)          }        }

Remote host involved in the render path

    • jitt.wwevents.fun

    Receives RemoteMessage GET requests and returns MESSAGE strings that the extension passes into Telegram Web toasts.

What it can do

Permissions this extension asks for, as declared in version 6.4. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 7.1, which we have not unpacked yet.

  • Read and change your data on telegram.org

    *://*.telegram.org/*

  • Read and change your data on oinkandstuff.com

    *://*.oinkandstuff.com/*

  • Read and change your data on every site you visit

    <all_urls>

  • Read information about your displays

    system.display

  • Run its own code inside the pages you visit

    scripting

  • Act on the current tab, but only after you click the extension

    activeTab

  • Show you desktop notifications

    notifications

  • Add items to the right-click menu

    contextMenus

  • Store an unlimited amount of data in your browser

    unlimitedStorage

  • Store data in your browser

    storage

Updated 2 November 2026kjnmdomccekpkjomjhapnilfmeiglkid