Is Corgi AI - Your language teacher safe?
Corgi AI ships hardcoded Voicemaker and Google Analytics credentials in its extension bundle.
The extension includes a Voicemaker bearer API key and Google Analytics Measurement Protocol secret in its installed JavaScript. Its text-to-speech requests send the Voicemaker key to developer.voicemaker.in, and analytics requests send the Google Analytics secret to www.google-analytics.com.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Corgi AI ships live Voicemaker and Google Analytics keys in its code
Corgi AI's background script contains a hardcoded Voicemaker API key and a Google Analytics API secret, identical in every install.
The Voicemaker key still worked when replayed outside the extension, confirming it is live.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
You select a word or phrase and click the pronunciation button to hear it read aloud.
The extension authenticates to a third-party text-to-speech vendor using an API key that is hardcoded into every copy of the extension.
The key is not generated per user or per session; it is the same value in every install.
HTTP 200 with success:true and a remaining-quota counter, confirming the key extracted from the shipped bundle is a live, currently valid, metered vendor credential that works with no login and no extension context.
- Content-Type
- application/json
- Authorization
- Bearer <redacted>
{ "VoiceId": "ai3-Jony", "LanguageCode": "en-US", "Text": "hello", "OutputFormat": "mp3", "SampleRate": "48000", "Effect": "default", "MasterSpeed": "0", "MasterVolume": "0", "MasterPitch": "0"}The vendor secrets object, inlined three separate times in the shipped bundle
// The bundler inlines this same object literal three separate times.// Written out once here for clarity; the shipped code repeats it verbatim.const VENDOR_SECRETS = { VOICEMAKER_API_KEY: "<redacted>", GA_MEASUREMENT_ID: "G-3RDMY7F6D8", GA_API_SECRET: "<redacted>",};headers['Authorization'] = `Bearer ${VENDOR_SECRETS.VOICEMAKER_API_KEY}`;fetch(`${GA_ENDPOINT}?measurement_id=${VENDOR_SECRETS.GA_MEASUREMENT_ID}&api_secret=${VENDOR_SECRETS.GA_API_SECRET}`, { method: 'POST', body: JSON.stringify({ client_id, events: [{ name: eventName, params: eventData }] }),});You use any normal feature of the extension, such as completing a translation or opening the popup.
The extension logs the usage event to Google Analytics using a measurement ID and API secret that are identical in every install.
The API secret is sent in plain text in the request URL to Google's Measurement Protocol endpoint.
- developer.voicemaker.in
Third-party text-to-speech vendor. Receives the hardcoded bearer key on every audio request, billed against Voicemaker's own metered usage plan.
- www.google-analytics.com
Google Analytics Measurement Protocol collector. Receives the hardcoded measurement ID and API secret with every usage event.
Scans a directory of extracted extension JavaScript for literal strings assigned to API-key- or credential-shaped variable names, the same pattern that exposed this extension's Voicemaker key.
- Node.js 18+
#!/usr/bin/env node// find-hardcoded-secrets.js// Usage: node find-hardcoded-secrets.js <path-to-extracted-extension-dir>const fs = require('fs');const path = require('path');const SECRET_NAME = /(API[_-]?KEY|SECRET|TOKEN|BEARER)/i;const ASSIGNMENT = /["']?([A-Za-z0-9_]{3,40})["']?\s*:\s*["']([A-Za-z0-9\-_.]{12,80})["']/g;function scanFile(filePath) { const text = fs.readFileSync(filePath, 'utf8'); const hits = []; let match; while ((match = ASSIGNMENT.exec(text)) !== null) { const [, name, value] = match; if (SECRET_NAME.test(name)) { hits.push({ name, value, index: match.index }); } } return hits;}function walk(dir, cb) { for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { const full = path.join(dir, entry.name); if (entry.isDirectory()) walk(full, cb); else if (/\.(js|mjs)$/.test(entry.name)) cb(full); }}const target = process.argv[2];if (!target) { console.error('Usage: node find-hardcoded-secrets.js <path-to-extracted-extension-dir>'); process.exit(1);}let total = 0;walk(target, (file) => { const hits = scanFile(file); for (const hit of hits) { total += 1; console.log(`${file}: ${hit.name} = "${hit.value.slice(0, 6)}..." (offset ${hit.index})`); }});console.log(`\n${total} candidate hardcoded secret(s) found.`);- 1node find-hardcoded-secrets.js <extension-dir>.
- 2Review each printed KEY/TOKEN hit.
- 3Revoke and reissue it server-side instead of embedding it client-side.
What it can do
Permissions this extension asks for, as declared in version 1.7.3. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on every site you visit
http://*/*
Read and change your data on every secure site you visit
https://*/*
See the address and title of every tab you have open
tabs
Store data in your browser
storage
Store an unlimited amount of data in your browser
unlimitedStorage
Sign you in with your Google account
identity
See the email address of your Google account
identity.email
Where it sends data
Destinations our analysis observed Corgi AI - Your language teacher 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.
- www.google-analytics.comwidely used
Corgi AI - Your language teacher sends data to www.google-analytics.com. A widely used service: 346 other extensions we have analysed send data here.
- developer.voicemaker.in
Corgi AI - Your language teacher sends data to developer.voicemaker.in. No other extension we have analysed sends data here.