Is Youtube Transcript AI Summary safe?
Youtube Transcript AI Summary is medium risk. Youtube Transcript AI Summary sent a GA Measurement Protocol event on options-page open: client UUID, event `chrome_options`, version `1.2.4`, browser `chrome`. The same helper fires on install, update, config, saves, errors, milestones.
Who publishes itPDF Solutions - 8 other listings from the same operator, none carrying a finding
PDF Solutions - 8 other listings from the same operator, none carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
8 other listings published from this account, 56k+ users between them, none of them carrying 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.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Usage UUID and feature events sent to Google Analytics
Youtube Transcript AI Summary sent a GA Measurement Protocol event on options-page open: client UUID, event `chrome_options`, version `1.2.4`, browser `chrome`.
The same helper fires on install, update, config, saves, errors, milestones.
You open the options page or use features that the extension records as analytics events.
Tracked code paths include install or update, API provider settings, prompt saves, API errors, successful summaries, and usage milestones.
The extension sends an event to Google Analytics with a reusable client ID.
Dynamic analysis captured the options-page event; the shipped background script uses the same helper for the other event names.
| Field | Value | Why it matters | |
|---|---|---|---|
Your analytics ID | 4fecc43f-fd53-4385-9c9e-51a1d3a842d0 | This lets repeated extension events be tied to the same browser profile over time. | |
The action label | chrome_options | This describes what you did in the extension at the time of the event. | |
Extension version | 1.2.4 | This identifies which release of the extension produced the event. | |
Browser family | chrome | This adds device context to the analytics event. | |
Usage milestones | chrome_usage_milestone_5 | The source code records summary-generation milestones, so the analytics stream can reflect how often you use the extension. |
| Content-Type | text/plain |
{
"client_id": "4fecc43f-fd53-4385-9c9e-51a1d3a842d0",
"events": [
{
"name": "chrome_options",
"params": {
"extension_version": "1.2.4",
"browser": "chrome"
}
}
]
}The background script creates the client ID and sends analytics events
function generateUUID() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(n) {
var t = Math.random() * 16 | 0,
i = n === "x" ? t : t & 3 | 8;
return i.toString(16)
})
}
async function install_notice() {
clientId = await getFromStorage("ga_client_id").ga_client_id;
clientId || (clientId = generateUUID(), setToStorage({
ga_client_id: clientId
}));
trackRequest(browser + "_session");
chrome.storage.sync.get(["status", "prokey", "site", "method", "apikey", "gemini_apikey", "claude_apikey"], function(n) {
if (n.status != null) {
n.method && trackRequest(browser + "_method_" + n.method);
n.apikey && n.apikey !== "" && trackRequest(browser + "_api_openai_configured");
n.gemini_apikey && n.gemini_apikey !== "" && trackRequest(browser + "_api_gemini_configured");
n.claude_apikey && n.claude_apikey !== "" && trackRequest(browser + "_api_claude_configured");
n.prokey != "" ? fetch(n.site + "/api", {
method: "POST",
headers: {
"Content-Type": "application/json",
prokey: n.prokey,
appid: chrome.runtime.id
},
body: JSON.stringify({
data: "some data"
})
}).then(n => n.json()).then(n => {
n.status && n.status === "expired" ? (chrome.storage.sync.set({
short: !0,
captions: !1,
prokey: "",
impdt: "",
chunk: 3e3,
claude_chunk: 75e3,
gemini_chunk: 3e3,
prompt: "Summarize the following text:\n{TEXT}\n\nSummary:",
first_prompt: "I will enter text in {TOTAL} parts and you will wait for them one by one, after last one you should then generate a summary, do not summarize each text I enter, do you understand?",
last_prompt: "\ntl;dr\n",
middle_prompt: "{CHUNK}\nWait for next part, and do not summarize.\n"
}, async function() {}), trackRequest(browser + "_expired")) : n.status && n.status === "ok" && n.prokey && n.impdt && trackRequest(browser + "_pro")
}).catch(n => {
console.error("Pro key validation error:", n)
}) : trackRequest(browser + "_free");
return
}
})
}async function trackRequest(n) {
console.log("Tracking event:", n);
try {
await fetch(url, {
method: "POST",
headers: {
"Content-Type": "text/plain"
},
body: JSON.stringify({
client_id: clientId,
events: [{
name: n,
params: {
extension_version: chrome.runtime.getManifest().version,
browser: browser
}
}, ]
})
}).then(() => {}).catch(n => {
console.error("Analytics tracking error:", n)
})
} catch (t) {
console.error("Analytics error:", t)
}
}chrome.runtime.onMessage.addListener(function(n, t, i) {
console.log("Background script message received:", n.message);
try {
if (n.message === "get-prompt" && chrome.storage.local.get("chatgpt", function(n) {
i({
prompt: n.chatgpt
})
}), n.message === "save-prompt" && (chrome.storage.local.set({
chatgpt: n.prompt
}), trackRequest(browser + "_prompt_saved")), n.message === "get-claude-data" && chrome.storage.local.get(["claude", "chapters", "dataId"], function(n) {
i({
claude: n.claude || "",
chapters: n.chapters || "",
dataId: n.dataId || ""
})
}), n.message === "save-claude-data" && (chrome.storage.local.set({
claude: n.claude || "",
chapters: n.chapters || "",
dataId: n.dataId || ""
}), trackRequest(browser + "_claude_data_saved")), n.type === "trackEvent") {
const {
event: t
} = n;
trackRequest(t)
}
if (n.message === "open-claude") {
console.log("Opening Claude interface");
const n = chrome.runtime.getURL("claude.html");
chrome.tabs.create({
url: n
});
trackRequest(browser + "_claude_opened")
}
if (n.message === "api-error") {
const {
provider: t,
error: i
} = n;
trackRequest(browser + "_api_error_" + t);
console.error(`API Error (${t}):`, i)
}
if (n.message === "summary-generated") {
const {
provider: t
} = n;
trackRequest(browser + "_summary_success_" + t);
chrome.storage.sync.get(["usage_count"], function(n) {
const t = (n.usage_count || 0) + 1;
chrome.storage.sync.set({
usage_count: t
});
[1, 5, 10, 25, 50, 100].includes(t) && trackRequest(browser + "_usage_milestone_" + t)
})
}
} catch (r) {
console.error("Background script error:", r)
}
return !0
});- www.google-analytics.com
Receives Measurement Protocol events containing the extension's persistent analytics UUID, event name, extension version, and browser.
What it can do
Permissions this extension asks for, as declared in version 1.2.4. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 1.3.1, which we have not unpacked yet.
Read and change your data on www.youtube.com
https://www.youtube.com/*
Read and change your data on chatgpt.com
https://chatgpt.com/*
Read and change your data on claude.ai
https://claude.ai/*
Read and change your data on aisummary.app
https://aisummary.app/*
Read and change your data on www.google-analytics.com
https://www.google-analytics.com/*
Read and change your data on spaces-downloader.freeconverting.com
https://spaces-downloader.freeconverting.com/*
Store data in your browser
storage
Watch every request your browser makes
webRequest
Schedule its own background tasks
alarms