Is AI翻訳 safe?
AI翻訳 (JATranslator) sends the URLs of every open browser tab to its own servers during meetings and whenever its popup is opened.
When a Google Meet session starts, the extension opens a WebSocket to its realtime backend and streams the URLs of all open tabs in every browser window, not just the meeting tab, tagged with the current session. Separately, each time the popup is opened it queries the active tab and every other open tab and posts the full list of URLs to the vendor's API, authenticated with the user's stored access token.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
This extension sends every open tab's URL to its server during Meet calls
Code analysis shows that once you join a Google Meet call, the extension's overlay opens a WebSocket to its own server and sends the URL of every open tab in every browser window, not just the meeting tab.
You join or start a Google Meet call while the extension's translation overlay is active.
The overlay's WebSocket opens, and the extension asks the browser for every open tab across every window, then sends the full URL list over that connection.
This runs automatically once per connection; there is no prompt naming the destination.
Unfiltered tab query wired to the open WebSocket
chrome.runtime.onMessage.addListener(((e, o, t) => {
n(void 0, void 0, void 0, (function*() {
if ("meetingMetrics" !== e) return;
const o = (yield chrome.tabs.query({})).map((e => e.url)).filter((e => void 0 !== e)).map((e => ({
href: e
})));
t(o)
}))
}))m4 = function() {
WebSocket.OPEN === this.readyState && (chrome.runtime.sendMessage("meetingMetrics", t => {
const e = new Date().toISOString(),
n = JSON.stringify({
event: "meetingMetrics",
timestamp: e
}),
i = JSON.stringify({
tabs: t,
version: "3.23.0",
build: "JATranslator"
}),
o = new Blob([n, "\n", i]);
setTimeout(() => {
this.send(o)
}, 1e4)
}), d4 = setInterval(() => { /* ping keepalive, every 20s */ }, 2e4), f4.on("update", t => { /* forwards Yjs doc updates too */ }))
}| Field | Value | Why it matters | |
|---|---|---|---|
Every open tab's URL | https://mail.google.com/mail/u/0/#inbox, https://online.chase.com/dashboard (illustrative) | The web address of each tab open anywhere in the browser, not limited to the Meet tab or its window. | |
Event tag | event: "meetingMetrics", timestamp: "2026-09-19T14:02:11.000Z" | The frame is labeled meetingMetrics, tying the tab list to whichever Meet call triggered it. | |
Extension version and build | version: "3.23.0", build: "JATranslator" | The installed extension's version string and internal build name travel with the tab list. |
- realtime.jotme.io
The vendor's own realtime backend; receives the full open-tab list once per Meet session over a connection opened with the signed-in user's access token.
Hooks WebSocket.prototype.send in the overlay's frame to flag the meetingMetrics frame this claim describes, so you can check for yourself whether it fires.
(function () {
const OrigWS = window.WebSocket;
function Wrapped(...args) {
const ws = new OrigWS(...args);
const origSend = ws.send.bind(ws);
ws.send = function (data) {
const inspect = (text) => {
if (text.includes("meetingMetrics")) {
console.warn("[meet-tab-broadcast-watch] frame sent:", text);
}
};
if (typeof data === "string") inspect(data);
else if (data instanceof Blob) data.text().then(inspect);
return origSend(data);
};
return ws;
}
Wrapped.prototype = OrigWS.prototype;
window.WebSocket = Wrapped;
console.log("[meet-tab-broadcast-watch] Hooked WebSocket. Reload the Meet tab and watch this console.");
})();- 1Join a Meet call with the extension enabled.
- 2In DevTools, switch console context to the overlay's frame.
- 3Paste and run this script.
- 4Reload the tab and watch the console for the logged frame.
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 this extension's popup sends every open tab's URL to its server
Code analysis shows that opening the extension's popup makes it query your active tab plus every other open tab in the window, then POST the full list to the vendor's own backend with your sign-in token attached.
You click the extension's toolbar icon to open its popup.
The popup asks the background script for your active tab's URL and every other open tab's URL, then posts the full list to the vendor's own server with your sign-in token attached.
This runs every time the popup mounts; there is no prompt naming the destination.
Popup mount call and the background handler that posts the tab list
if (Me.popup) {
const mt = Me.popup.sessionId || BT();
bn(mt) /* ... restore saved chat/translate state ... */;
Am.send({
chatSessionId: mt,
type: "Open"
})
}s = e => {
const { message: o } = e, t = r(e, ["message"]);
n(void 0, void 0, void 0, (function*() {
if ("popupMetrics" !== o) return;
const n = yield chrome.tabs.query({ active: !0, currentWindow: !0 }),
r = (yield chrome.tabs.query({})).filter((e => e?.id !== n?.[0]?.id));
if ("Close" !== t.type) {
t.activeTabURL = n?.[0]?.url;
t.otherTabs = r.map((e => e?.url)).filter((e => void 0 !== e)).map((e => ({ href: e })))
}
const i = (yield chrome.storage.local.get("access")).access;
fetch("https://api.jotme.io/jotme/popup/metrics", {
method: "POST",
body: JSON.stringify(t),
headers: { Authorization: `Bearer ${i}` }
})
}))
}| Field | Value | Why it matters | |
|---|---|---|---|
Active tab's URL | activeTabURL: "https://drive.google.com/drive/u/0/folders/1AbC" (illustrative) | The address of whichever tab you have focused when you open the popup. | |
Every other open tab's URL | otherTabs: [{href:"https://mail.google.com/mail/u/0/"}, {href:"https://online.chase.com/dashboard"}] (illustrative) | The address of every other tab open in that window, sent as a list alongside the active one. | |
Sign-in access token | Authorization: Bearer eyJhbGciOi... (illustrative) | The same token that authenticates your account is attached to this request as a bearer header. |
- api.jotme.io
The vendor's own backend; receives the active tab's URL and every other open tab's URL each time the popup opens, authenticated with the signed-in user's access token.
Hooks fetch() inside the extension's service worker to flag the POST this claim describes, so you can check for yourself whether it fires.
(function () {
const target = "api.jotme.io/jotme/popup/metrics";
const origFetch = self.fetch;
self.fetch = function (input, init) {
const url = typeof input === "string" ? input : input && input.url;
if (url && url.includes(target)) {
console.warn("[popup-metrics-watch] POST to", url, init && init.body);
}
return origFetch.apply(this, arguments);
};
console.log("[popup-metrics-watch] Hooked fetch() in the service worker. Open the popup and watch this console.");
})();- 1In chrome://extensions, enable Developer mode, then click the extension's 'service worker' link.
- 2Paste and run this script there.
- 3Click the toolbar icon to open the popup.
- 4Watch the console for the logged POST.
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.