Is AI Translator safe?
AI Translator sends the URLs of every open browser tab, across all windows, to JotMe's servers on popup open or Google Meet calls.
When the user opens the extension's popup or joins a Google Meet call while signed in, AI Translator queries the full list of open tabs across every browser window (not just the active one) and transmits their URLs to JotMe's backend. On Google Meet calls this goes over an authenticated WebSocket to realtime.jotme.io; from the popup it is sent via an HTTPS POST to api.jotme.io, in both cases carrying the signed-in user's access token.
Strong link to a flagged operator: 2 of the 3 other listings from the same operator are rated high or critical for malicious behavior. Flagged: JotMe (high), google-meet%E3%81%A7%E3%81%AEai%E7%BF%BB%E8%A8%B3%E3%80%81%E6%96%87%E5%AD%97%E8%B5%B7%E3%81%93%E3%81%97%E3%80%81%E8%AD%B0 (high).
Who publishes itJotme Limited - 3 other listings from the same operator, 3 of them carrying a finding
Jotme Limited - 3 other listings from the same operator, 3 of them carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
3 other listings published from this account, 11k+ users between them. 3 of them carry a finding.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Joining a Google Meet call sends the URLs of every open tab to JotMe
Code analysis shows that once you join a Google Meet call while signed in to JotMe, the extension opens a WebSocket to JotMe's server and sends the URLs of every open tab across all your browser windows, not just Meet or JotMe tabs.
What actually happens
You join a Google Meet call while signed in to the JotMe extension.
The extension collects the URL of every open browser tab, in every window, and sends the list to JotMe's server over a WebSocket.
The send happens automatically about 10 seconds after the WebSocket connects, with no prompt.
Fields in the meetingMetrics WebSocket message
- URL of every open tabhttps://mail.google.com/mail/u/0/#inbox
Covers tabs on any site, in any window, open at the time of the call; not limited to Google Meet or jotme.io.
- Your access tokeneyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyXzQ0MTIifQ.abc123signature
Included in the WebSocket connection itself, so the server can tie the tab list to your signed-in JotMe account.
- Extension build3.23.0 / ENTranslator
Identifies which version of the extension collected the data.
The code that does this
Tab collection and WebSocket send (meetingMetrics)
const p4 = (t, e) => function() { const n = new CustomEvent("displayMultipleWSWarning"); window.dispatchEvent(n), clearInterval(d4), setTimeout(() => { const r = encodeURIComponent(window.location.pathname); if (!e.access) return; const i = new WebSocket("wss://realtime.jotme.io", [e.access, r]); i.onopen = m4, i.onmessage = g4, i.onerror = b4, i.onclose = p4(t, e), t(i) }, 1e4)};const m4 = function() { WebSocket.OPEN === this.readyState && chrome.runtime.sendMessage("meetingMetrics", t => { const e = new Date().toISOString(); const n = JSON.stringify({ event: "meetingMetrics", timestamp: e }); const i = JSON.stringify({ tabs: t, version: "3.23.0", build: "ENTranslator" }); const o = new Blob([n, "\n", i]); setTimeout(() => { this.send(o) }, 1e4) })};// service-worker.js: answers the 'meetingMetrics' message with every open tab, any windowchrome.runtime.onMessage.addListener((e, o, t) => { if (e !== "meetingMetrics") return; chrome.tabs.query({}).then(tabs => t(tabs.map(tab => ({ href: tab.url }))))});Where the tab list is sent
- realtime.jotme.io
JotMe's own realtime backend. Receives the WebSocket message containing every open tab's URL, authenticated with your access token.
Observation
Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.
Opening JotMe's popup sends the URLs of every other open tab to its server
Code analysis shows that opening JotMe's toolbar popup, or starting a new chat, makes the extension query every open tab across all windows and POST the active tab's URL plus every other tab's URL to JotMe's server.
What actually happens
You open the JotMe extension's popup, or start a new chat inside it.
The extension collects the active tab's URL plus the URLs of every other open tab in every window, and POSTs the list to JotMe's server.
This happens on every popup open and every new chat, not just once per session.
Fields in the popupMetrics POST body
- Active tab's URLhttps://docs.google.com/document/d/1AbCkX/edit
The page you were looking at when you opened the popup.
- URLs of your other open tabs[{"href":"https://mail.google.com/mail/u/0/#inbox"},{"href":"https://github.com/acme/internal-repo"}]
Every other tab open in any browser window at that moment, on any site.
- Your access tokenBearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyXzQ0MTIifQ.abc123signature
Sent as an Authorization header so the server can tie this tab list to your signed-in account.
The code that does this
Tab collection and POST on popup open (popupMetrics)
class Am {}Am.send = (o) => { const parsed = schema.safeParse(o); if (!parsed.success) return; chrome.runtime.sendMessage({ message: "popupMetrics", ...parsed.data });};// call sites in the popup UIAm.send({ chatSessionId, type: "NewChat" }); // starting a new chatAm.send({ chatSessionId, type: "Open" }); // popup mounted// service-worker.js message handlerchrome.runtime.onMessage.addListener(async (msg) => { if (msg.message !== "popupMetrics") return; const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true }); const otherTabs = (await chrome.tabs.query({})) .filter(t => t.id !== activeTab?.id); if (msg.type !== "Close") { msg.activeTabURL = activeTab?.url; msg.otherTabs = otherTabs.map(t => ({ href: t.url })); } const { access } = await chrome.storage.local.get("access"); await fetch("https://api.jotme.io/jotme/popup/metrics", { method: "POST", body: JSON.stringify(msg), headers: { Authorization: `Bearer ${access}` } });});Where the tab list is sent
- api.jotme.io
JotMe's own API. Receives the active tab URL and every other open tab's URL on every popup open, authenticated with your access token.
Observation
Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.
Where it sends data
Destinations our analysis observed AI Translator 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.
- realtime.jotme.io
AI Translator sends data to realtime.jotme.io. One other extension we have analyzed sends data here.
- api.jotme.io
AI Translator sends data to api.jotme.io. No other extension we have analyzed sends data here.
