Is AI Translator safe?

Medium risk

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.

JotMev3.23.0Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI FOUND

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.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You join a Google Meet call while signed in to the JotMe extension.

The extension did this

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.

02EvidenceFIELD TABLE
Fields in the meetingMetrics WebSocket message
FieldValueWhy it matters
URL of every open tab
https://mail.google.com/mail/u/0/#inboxCovers tabs on any site, in any window, open at the time of the call; not limited to Google Meet or jotme.io.
Your access token
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyXzQ0MTIifQ.abc123signatureIncluded in the WebSocket connection itself, so the server can tie the tab list to your signed-in JotMe account.
Extension build
3.23.0 / ENTranslatorIdentifies which version of the extension collected the data.
03EvidenceCODE COMPARE
The code that does this

Tab collection and WebSocket send (meetingMetrics)

What it actually does
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 window
chrome.runtime.onMessage.addListener((e, o, t) => {
  if (e !== "meetingMetrics") return;
  chrome.tabs.query({}).then(tabs => t(tabs.map(tab => ({ href: tab.url }))))
});
04EvidenceTHIRD PARTY LIST
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.

05EvidencePLAIN NOTE
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.

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI FOUND

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.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You open the JotMe extension's popup, or start a new chat inside it.

The extension did this

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.

02EvidenceFIELD TABLE
Fields in the popupMetrics POST body
FieldValueWhy it matters
Active tab's URL
https://docs.google.com/document/d/1AbCkX/editThe 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 token
Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyXzQ0MTIifQ.abc123signatureSent as an Authorization header so the server can tie this tab list to your signed-in account.
03EvidenceCODE COMPARE
The code that does this

Tab collection and POST on popup open (popupMetrics)

What it actually does
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 UI
Am.send({ chatSessionId, type: "NewChat" });   // starting a new chat
Am.send({ chatSessionId, type: "Open" });      // popup mounted

// service-worker.js message handler
chrome.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}` }
  });
});
04EvidenceTHIRD PARTY LIST
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.

05EvidencePLAIN NOTE
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.

Data recipients

realtime.jotme.ioapi.jotme.io
Updated 20 September 2026hhappcbkglbedakgldofnaepeaaijolk