Is Web for Telegram safe?
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.
Who publishes itwwevents - 17 other listings from the same operator, none carrying a finding
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.
Same store account
17 other listings published from this account, 48k+ users between them, none of them carrying 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.
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
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.
What actually happens
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 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
Captured request
Concrete fields and parameters in the remote-message path
- Remote message hostjitt.wwevents.fun
This is the server your browser contacts before the message is shown in Telegram Web.
- Application selectorChromeGen2eric
This value tells the server which message set the extension is requesting.
- Message fieldjsonData[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
Service worker fetches RemoteMessage records
eventPage.jsswitch (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.jsfunction 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.jsfunction 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">×</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