Is Youtube Transcript AI Summary safe?

Medium risk

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.

PDF Solutionsv1.3.1Chrome Web Store
45Risk
Who publishes it

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.

Publisher
PDF Solutions

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.

selectize.dev
Also called by 4 other listings, including DesignFiles Product Clipper, Youtube Transcript AI Summary

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

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

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.

01EvidenceCAUSE EFFECT
What actually happens
You did this

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 did this

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.

02EvidenceFIELD TABLE
Fields observed in the Google Analytics event
FieldValueWhy it matters
Your analytics ID
4fecc43f-fd53-4385-9c9e-51a1d3a842d0This lets repeated extension events be tied to the same browser profile over time.
The action label
chrome_optionsThis describes what you did in the extension at the time of the event.
Extension version
1.2.4This identifies which release of the extension produced the event.
Browser family
chromeThis adds device context to the analytics event.
Usage milestones
chrome_usage_milestone_5The source code records summary-generation milestones, so the analytics stream can reflect how often you use the extension.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://www.google-analytics.com/mp/collect?measurement_id=G-X2VV9MRTKB
Observed during dynamic analysis as a Google Analytics Measurement Protocol request.
Headers
Content-Typetext/plain
Body
{
  "client_id": "4fecc43f-fd53-4385-9c9e-51a1d3a842d0",
  "events": [
    {
      "name": "chrome_options",
      "params": {
        "extension_version": "1.2.4",
        "browser": "chrome"
      }
    }
  ]
}
04EvidenceCODE COMPARE
The code that does this

The background script creates the client ID and sends analytics events

What it actually does
Readable client ID creation and startup/configuration eventsbackground.js
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
    }
  })
}
Readable Measurement Protocol POST helperbackground.js
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)
  }
}
Readable message events and usage milestonesbackground.js
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
});
05EvidenceTHIRD PARTY LIST
Analytics destination
  • 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

Updated 30 September 2026eciiehmejcjnbooihpiljfnklkopkfcj