Is Google Reader: Free Natural AI Text to Speech, TTS safe?
Google Reader sends captured Google TTS tokens and page or selected text to an Appspot proxy for speech synthesis.
When text-to-speech runs, the extension captures a token from Google text-to-speech requests and stores it locally for later use. It sends that token together with selected text, page text, or uploaded document text to an Appspot proxy that forwards requests to Google Text-to-Speech.
Who publishes itDemocratic Developer - no other listings under this identity
Democratic Developer - no other listings under this identity
What this publisher told the store about itself, and the other listings that told it the same thing.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
TTS proxy receives Google token and narrated text
When you ask the extension to read text aloud, it takes a Google TTS token from gstatic and sends the narrated text to an Appspot proxy with that token in the query.
Both leave via cxl-services.appspot.com, only on TTS requests.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
You ask the extension to read selected text, page text, an uploaded document, or a sample voice aloud.
The extension also exposes context-menu entries for sending the selected text or all text on the current page to Google Reader.
The extension posts that text to an Appspot proxy and appends a Google text-to-speech token to the request URL.
The token is collected from the gstatic text-to-speech page and stored as gr/token before the POST is made.
- Google speech tokentoken=ya29.a0AfH6SMB7F9vX6k2wQpN8r3mL1sT4uV5xY6zA7bC8dE9fG0hI1jK2lM3nO4pQ5rS6tU7vW8xY9z (illustrative)
This request credential is added to the proxy URL for the speech request you triggered.
- Narrated textQuarterly planning notes for the July 18 product launch (illustrative)
The text you chose for narration is placed in the request body, so page, form, or document content can be included.
- Voice and languagevoice.name=en-US-Chirp3-HD-Achernar, languageCode=en-US (from shipped defaults)
The request also includes the voice and language settings used to generate the audio.
Shipped code path that captures the token and sends narrated text
Injected token listener
injected.js(() => { 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; }})();Token storage hook
assets/index.tsx-DRtiKFi_.jsfunction useGoogleReaderToken() { const [token, setToken] = useState(null); const [isAuthenticated, setIsAuthenticated] = useState(false); function saveToken(value) { setToken(value); setIsAuthenticated(Boolean(value)); localStorage.setItem("gr/token", value); } function onAuthReceived(event) { const accessToken = event.detail.accessToken; saveToken(accessToken.includes("Bearer") ? accessToken.split(" ")[1] : accessToken); } useEffect(() => { window.dispatchEvent(new CustomEvent("GET_TOKEN")); window.addEventListener("AUTH_RECEIVED", onAuthReceived); return () => window.removeEventListener("AUTH_RECEIVED", onAuthReceived); }, []); return { token, isAuthenticated };}Proxy endpoint constant
assets/preload-helper-C9jHGiCF.jsconst EVENTS = { AUTH_RECEIVED: "AUTH_RECEIVED", GET_TOKEN: "GET_TOKEN", CHUNK_RECEIVED: "CHUNK_RECEIVED", CHUNK_SEND: "CHUNK_SEND"};const TTS_PROXY_URL = "https://cxl-services.appspot.com/proxy?url=https://texttospeech.googleapis.com/v1beta1/text:synthesize";Text-to-speech POST builder
assets/index.tsx-DRtiKFi_.jsfunction buildSpeechBody(text, voiceName, languageCode, modelName, prompt) { const body = { audioConfig: { audioEncoding: "LINEAR16", pitch: 0, speakingRate: 1 }, input: { text }, voice: { languageCode, name: voiceName, modelName } }; if (prompt && prompt.trim()) { body.input.prompt = prompt.trim(); } return JSON.stringify(body);}async function fetchSpeechAudio(text, chunkNumber) { let token = localStorage.getItem("gr/token"); if (!token) { token = await waitForToken(); } if (!token) { showTokenError(); return; } const url = `${TTS_PROXY_URL}&token=${token}`; const response = await fetch(url, { method: "POST", body: buildSpeechBody(text), signal: AbortSignal.timeout(30000) }); const json = await response.json(); return decodeAudioContent(json.audioContent);}Context-menu text collection
assets/index.ts-DEiWyd7n.jsasync function openReaderWithText(text) { await chrome.storage.local.set({ origin: true }); await chrome.storage.local.set({ selectedText: text }); await chrome.storage.local.set({ iswebreader: Date.now() }); const tabId = await openOrFocusGstaticReader(); chrome.tabs.sendMessage(tabId, { type: "OPEN_POPUP", payload: "ORIGIN_VERIFIED" });}function getWholePageText() { return document.body.innerText;}chrome.contextMenus.create({ id: "customContextMenu", parentId: "googleReaderParent", title: "Send selected text to Google Reader", contexts: ["selection"]});chrome.contextMenus.create({ id: "customContextMenuAll", parentId: "googleReaderParent", title: "Send all text on current page to Google Reader", contexts: ["page", "selection"]});- cxl-services.appspot.com
Google App Engine host that receives the tokenized POST from the extension.
- texttospeech.googleapis.com
Google Cloud Text-to-Speech API named in the proxy URL parameter.
What it can do
Permissions this extension asks for, as declared in version 2.2.5. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
See the address and title of every tab you have open
tabs
Store data in your browser
storage
Show you desktop notifications
notifications
Act on the current tab, but only after you click the extension
activeTab
Add items to the right-click menu
contextMenus
Run its own code inside the pages you visit
scripting
Where it sends data
Destinations our analysis observed Google Reader: Free Natural AI Text to Speech, TTS 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.
- texttospeech.googleapis.com
Google Reader: Free Natural AI Text to Speech, TTS sends data to texttospeech.googleapis.com. One other extension we have analysed sends data here.
- cxl-services.appspot.com
Google Reader: Free Natural AI Text to Speech, TTS sends data to cxl-services.appspot.com. No other extension we have analysed sends data here.