Is Text to Speech Google Docs safe?

Medium risk

Text to Speech Google Docs sends page hostname and a snippet of the text you read aloud to Amplitude on every TTS session.

Each time you start a text-to-speech session, the extension transmits an analytics event to Amplitude that includes the site hostname, the first 32 characters of the text being read, the detected language, voice settings, and a stable anonymous user ID persisted across sessions. A secondary event also sends a normalised URL path pattern. Both identifiers — an anonymousUserId UUID and a device ID stored in chrome.storage.local — are created once and never rotated, enabling cross-session tracking.

Winston Hv2.1.0Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

TTS Sessions Send Text Excerpts to Amplitude

MediumunwantedCWE-359Dynamic sandbox

Starting a text-to-speech session builds an Amplitude event with the page host, first 32 characters of the first text chunk, voice/rate settings, language/length metadata, and persistent anonymous user/device IDs.

What actually happens

You did this

You start text-to-speech on the current page.

The extension did this

The extension prepares an analytics event that includes where the reading happened and a short excerpt of the text.

Fields built for the Amplitude session event

  • Page host
    docs.google.com (illustrative)

    Shows which website was open when you started the reading session.

  • Text excerpt
    Quarterly planning notes for (illustrative)

    Includes the first 32 characters of the first text chunk read by the extension.

  • Anonymous user ID
    550e8400-e29b-41d4-a716-446655440000 (illustrative)

    Lets separate reading sessions be tied to the same browser profile over time.

  • Device ID
    dev_1720818929123_q9x4m2ka (illustrative)

    Adds another stable identifier for the browser where the extension is installed.

  • Voice and reading settings
    voice_name: Google US English, rate: 1, text_lang_detected: en, text_length: 1842 (illustrative)

    Describes how the reading session was configured.

  • Normalized URL path
    /document/d/{ID}/edit (illustrative)

    For extraction-result analytics, this records a generalized page path pattern instead of the full path.

Captured request

POSThttps://api2.amplitude.com/2/httpapi

200 OK observed during dynamic analysis; recorded request and response bodies were empty.

Headers
Content-Type
application/json

The code that does this

Session event construction and Amplitude submission

Readable version

Readable session-start fields

assets/player-CmA0CLvy.js
const s = (() => {    try {      return t.trackUrl ? new URL(t.trackUrl).host : ""    } catch {      return ""    }  })(),  m = t.source ?? X(s),  k = await ht(),  N = Date.now(),  y = k !== null && N - k < 6e4,  M = k === null ? 0 : Math.floor((N - k) / (1e3 * 60 * 60 * 24)),  Y = t.paragraphs.reduce((z, ct) => z + ct.text.length, 0),  F = (((i = C[0]) == null ? void 0 : i.text) ?? "").slice(0, 32);t.textFirstChars = F, I(v.SESSION_START, {  [o.SOURCE]: m,  [o.HOST]: s,  [o.VOICE_NAME]: t.settings.voiceName ?? "",  [o.VOICE_LANG]: t.resolvedVoiceLang,  [o.RATE]: t.settings.rate,  [o.TEXT_LENGTH]: Y,  [o.TEXT_LANG_DETECTED]: t.pageLang ?? "",  [o.VOICE_LANG_MATCHES_PAGE]: J(t.resolvedVoiceLang, t.pageLang),  [o.TEXT_FIRST_CHARS]: F,  [o.HOUR_OF_DAY]: new Date().getHours(),  [o.IS_CONTINUATION]: y,  [o.DAYS_SINCE_LAST_SESSION]: M}), (async () => await pt({  sessionId: await ft(),  startedAt: t.startedAt,  lastActivityAt: Date.now(),  source: m,  host: s,  atPct: 0}))();

Readable Amplitude POST sender

assets/sentry-CSvuoUnh.js
const Ni = "https://api2.amplitude.com/2/httpapi";async function Ai(e, t, n) {  var a;  const [r, s, o] = await Promise.all([yi(), pn(), Ii()]),    i = typeof chrome < "u" && ((a = chrome.runtime) != null && a.getManifest) ? chrome.runtime.getManifest().version : "unknown";  return {    user_id: r,    device_id: s,    session_id: (n == null ? void 0 : n.sessionId) ?? o,    time: (n == null ? void 0 : n.time) ?? Date.now(),    event_type: e,    app_version: i,    platform: "Chrome Extension",    event_properties: {      ...t,      product: "tts-gd"    }  }}async function ua(e, t = {}, n) {  const r = "8f4716e33e96712ef837134a2d3f25b8";  try {    const s = await Ai(e, t, n),      o = await fetch(Ni, {        method: "POST",        headers: {          "Content-Type": "application/json"        },        body: JSON.stringify({          api_key: r,          events: [s]        }),        keepalive: !0      });    o.ok || console.warn("[TTS GD] analytics track non-2xx", e, o.status)  } catch (s) {    console.warn("[TTS GD] analytics track failed", e, s)  }}

Readable persistent analytics identifiers

assets/sentry-CSvuoUnh.js
const se = "anonymousUserId";async function yi() {  const e = await chrome.storage.local.get([se]);  if (typeof e[se] == "string") return e[se];  const t = crypto.randomUUID();  return await chrome.storage.local.set({    [se]: t  }), t}const k = "ttsgd_amplitude_device_id",  kt = "ttsgd_amplitude_session_id",  oe = "ttsgd_unique_voices_tried",  $e = "ttsgd_last_session_end_at",  He = "ttsgd_install_action_tracker",  pe = "ttsgd_hanging_session";function Ie() {  try {    return typeof localStorage < "u"  } catch {    return !1  }}function Ti() {  const e = Math.random().toString(36).slice(2, 10);  return `dev_${Date.now()}_${e}`}async function pn() {  if (Ie()) {    const n = localStorage.getItem(k);    if (n) return n  }  const e = await chrome.storage.local.get([k]);  if (typeof e[k] == "string") {    const n = e[k];    return Ie() && localStorage.setItem(k, n), n  }  const t = Ti();  return await chrome.storage.local.set({    [k]: t  }), Ie() && localStorage.setItem(k, t), t}

External analytics destinations used by this code path

    • api2.amplitude.com

    Receives the text-to-speech session event through the /2/httpapi endpoint.

Where it sends data

Destinations our analysis observed Text to Speech Google Docs 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.

  • api2.amplitude.comwidely used

    Text to Speech Google Docs sends data to api2.amplitude.com. A widely used service: 42 other extensions we have analyzed send data here.

Updated 2 November 2026gfnpoanknpcndnafohkeooladnhphone