Is Google Reader: Free AI Text to Speech with Natural Voices, TTS safe?

High risk

Google Reader: Free AI Text to Speech with Natural Voices, TTS is high risk. Running text-to-speech on gstatic pages, a page-injected script reads token values from page request URLs.…

democraticdeveloperv2.2.5Chrome Web Store
72Risk
Who publishes it

democraticdeveloper - 2 other listings from the same operator, 1 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

2 other listings published from this account, 31k+ users between them. 1 of them carries a finding.

Shared hosts - 1 hostname

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.

readeon.com
Also called by 9 other listings, including Google Reader: Free Natural AI Text to Speech, TTS, ChatGPT Reader & Transcriber: Free AI Text↔Speech

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

Publishers can request a review.

Findings

Gstatic token forwarded to Appspot TTS proxy

Running text-to-speech on gstatic pages, a page-injected script reads token values from page request URLs.

The content script stores the token locally, then appends it to an Appspot proxy request that also carries the text being converted.

Severity
High unwanted
Type
Unexpected
CWE
CWE-522
Source
Dynamic sandbox
What actually happens
You did this

You use the extension to turn selected or entered text into speech.

The workflow runs on the Google-hosted text-to-speech page matched by the extension manifest.

The extension did this

The extension stores a token from the page and sends it with the text request to an Appspot proxy.

The request goes to cxl-services.appspot.com before reaching Google's text-to-speech API.

Data carried into the proxy request
  • Google request token
    ya29.a0AfH6SMD6j9v7TqR7example (illustrative)

    This can tie the text-to-speech request to the browser session that produced the token.

  • Text for playback
    Hi, this is a sample of how I sound

    The words you ask the extension to read are sent out for audio generation.

  • Proxy destination
    https://cxl-services.appspot.com/proxy?url=https://texttospeech.googleapis.com/v1beta1/text:synthesize

    Your request is routed through a third-party Appspot host rather than going directly from the page to Google Text-to-Speech.

Captured request
POSThttps://cxl-services.appspot.com/proxy?url=https://texttospeech.googleapis.com/v1beta1/text:synthesize&token=<redacted>
What's stored on your device

The token remains available to the extension after it is received, so later text-to-speech requests can reuse it.

Location
localStorage key gr/token
Contents
{  "gr/token": "ya29.a0AfH6SMD6j9v7TqR7example (illustrative)"}
The code that does this

Source path from gstatic token to Appspot request

Readable version

Manifest behavior in readable form

manifest.json
const contentScriptScope = {  matches: ["https://www.gstatic.com/*"],  documentStartScript: "assets/inject.tsx-loader-W682AOg9.js",  documentEndScript: "assets/index.tsx-loader-D7hqUahY.js",  webAccessibleInterceptor: "injected.js"};

Readable page interceptor

injected.js
function installXhrTokenInterceptor() {  const originalOpen = XMLHttpRequest.prototype.open;  const originalSend = XMLHttpRequest.prototype.send;  XMLHttpRequest.prototype.open = function (method, url, async, user, password) {    this._requestUrl = url;    return originalOpen.apply(this, arguments);  };  XMLHttpRequest.prototype.send = function (body) {    const accessToken = extractTokenFromURL(this._requestUrl);    if (accessToken) {      window.dispatchEvent(new CustomEvent("AUTH_RECEIVED", {        detail: { accessToken }      }));    }    return originalSend.call(this, body);  };  function extractTokenFromURL(url) {    const match = url.match(/token=([^&]+)/);    return match ? match[1] : null;  }}

Readable token listener and storage path

assets/index.tsx-Cr7MxVSB.js
function useGoogleReaderToken() {  const [token, setToken] = useState(null);  const [isAuthenticated, setIsAuthenticated] = useState(false);  function storeToken(rawToken) {    setToken(rawToken);    setIsAuthenticated(Boolean(rawToken));    localStorage.setItem("gr/token", rawToken);  }  function onAuthReceived(event) {    const accessToken = event.detail.accessToken;    if (accessToken.includes("Bearer")) {      storeToken(accessToken.split(" " )[1]);      return;    }    storeToken(accessToken);  }  useEffect(() => {    window.dispatchEvent(new CustomEvent("GET_TOKEN"));    window.addEventListener("AUTH_RECEIVED", onAuthReceived);    window.addEventListener("beforeunload", () => localStorage.removeItem("gr/token"));    return () => {      window.removeEventListener("AUTH_RECEIVED", onAuthReceived);    };  }, []);  return { token, isAuthenticated };}

Readable text-to-speech proxy request

assets/index.tsx-Cr7MxVSB.js and assets/utils-DwjuBbx-.js
const proxyUrl = "https://cxl-services.appspot.com/proxy?url=https://texttospeech.googleapis.com/v1beta1/text:synthesize";async function postTextToSpeech(url, text, languageCode, voiceName) {  const body = JSON.stringify({    audioConfig: {      audioEncoding: "LINEAR16",      effectsProfileId: ["medium-bluetooth-speaker-class-device"],      pitch: 0,      speakingRate: 1    },    input: { text },    voice: {      languageCode: languageCode ?? "en-US",      name: voiceName ?? "en-US-Chirp3-HD-Achernar"    }  });  return fetch(url, { method: "POST", body });}async function handleFetchAudio(text, chunkIndex, fallbackToken) {  const token = localStorage.getItem("gr/token") || fallbackToken;  if (chunkIndex !== null && chunkIndex >= 0 && token) {    return postTextToSpeech(`${proxyUrl}&token=${token}`, text);  }}
External services involved in the text-to-speech path
    • cxl-services.appspot.com

    Receives the extension's POST request with the token query parameter and text-to-speech payload.

    • texttospeech.googleapis.com

    Target Google Text-to-Speech API URL embedded inside the Appspot proxy request.

Updated 30 September 2026jdmmahggnlemcmcoljocaoejjlkjknld