Is Website Downloader safe?

Medium risk

Website Downloader is medium risk. Opening Website Downloader or finishing a download posts a telemetry event to Tinybird. A side-panel event carried a persistent UUID, language, timezone, version, platform, browser, user-agent; download events also add the page URL.

feedforward.softwarev4.0.2Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

Download events send page URLs to Tinybird

Opening Website Downloader or finishing a download posts a telemetry event to Tinybird.

A side-panel event carried a persistent UUID, language, timezone, version, platform, browser, user-agent; download events also add the page URL.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You open the extension panel or complete a page download.

The extension did this

The extension sends a telemetry event to Tinybird with your browser context and, for download events, the downloaded page URL.

Captured request
POSThttps://api.europe-west2.gcp.tinybird.co/v0/events?name=analytics_events
Headers
Content-Type
application/json
Authorization
Bearer <redacted>
Telemetry fields assembled by the extension
  • Event name
    panel.opened

    Shows which extension action happened, such as opening the panel or finishing a download.

  • Persistent user ID
    7d45c0de-9d3e-4b69-9a40-3adf3b3476f6 (illustrative)

    Lets the analytics service tie separate extension events back to the same browser profile.

  • Downloaded page URL
    https://docs.example.com/private/project-plan.html (illustrative)

    Shows which page you downloaded and can reveal the site, path, and query string for that page.

  • Language and timezone
    en-US, UTC

    Adds regional context to your event and can narrow down where and how the browser is used.

  • Browser and platform
    Chromium on Linux, extension version 3.0.24

    Describes your browser environment and can help distinguish one device profile from another.

  • User agent
    Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36 (illustrative)

    Provides detailed browser and operating-system information as part of the telemetry event.

The code that does this

The shared telemetry sender builds the JSON event and posts it to Tinybird

Readable version

Readable side-panel sender and metadata collector

chunks/sidepanel-B-KAhS5d.js
const TINYBIRD_LOCAL_ENDPOINT = "http://localhost:7181/v0/events?name=analytics_events",  TINYBIRD_CLOUD_ENDPOINT = "https://api.europe-west2.gcp.tinybird.co/v0/events?name=analytics_events",  TINYBIRD_LOCAL_TOKEN = "<redacted>",  TINYBIRD_CLOUD_TOKEN = "<redacted>";async function getOrCreateUserId() {  const M = await chrome.storage.sync.get("user_id");  if (M.user_id) return M.user_id;  const F = crypto.randomUUID();  return await chrome.storage.sync.set({    user_id: F  }), F}async function collectMetadata() {  const M = await detectBrowser(),    F = navigator.userAgentData;  return {    timestamp: new Date().toISOString(),    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,    language: navigator.language,    extensionId: chrome.runtime.id,    version: chrome.runtime.getManifest().version,    platform: F?.platform || navigator.platform,    browser: M,    userAgent: navigator.userAgent,    isProduction: !0  }}async function tbEvent(M, F = {}) {  try {    const U = await getOrCreateUserId(),      Z = await collectMetadata(),      rt = {        event_name: M,        userId: U,        timestamp: Z.timestamp,        timezone: Z.timezone,        language: Z.language,        extensionId: Z.extensionId,        version: Z.version,        platform: Z.platform,        browser: Z.browser,        userAgent: Z.userAgent,        isProduction: Z.isProduction ? 1 : 0,        ...F      },      pt = isDevelopmentMode();    await fetch(pt ? TINYBIRD_LOCAL_ENDPOINT : TINYBIRD_CLOUD_ENDPOINT, {      method: "POST",      headers: {        Authorization: `Bearer ${pt?TINYBIRD_LOCAL_TOKEN:TINYBIRD_CLOUD_TOKEN}`,        "Content-Type": "application/json"      },      body: JSON.stringify(rt)    })  } catch (U) {    console.error("Failed to send event to Tinybird:", U)  }}
The code that does this

Download completion adds the page URL before sending telemetry

Readable version

Readable download completion path

chunks/sidepanel-B-KAhS5d.js
async function downloadPreparedData(M) {  const F = M.downloadableInfo?.options,    U = M.url;  F !== void 0 ? (await initiateDownload({    info: F  }), tbEvent("download.success", {    isMultiPage: M.downloadableInfo?.isMultiPage,    maxDepth: M.downloadableInfo?.maxDepth,    url: U  })) : tbEvent("download.object_missing", {    isMultiPage: M.downloadableInfo?.isMultiPage,    maxDepth: M.downloadableInfo?.maxDepth,    url: U  }), M.downloadableInfo && (M.downloadableInfo.isMultiPage ? tbEvent("download.multi_page", {    maxDepth: M.downloadableInfo.maxDepth,    url: U  }) : tbEvent("download.single_page")), M.downloadableInfo?.quotaExceeded ? infoMessage.setMessage("This website is too large to save completely.<br>Download incomplete - some pages could not be saved.", !1, !1) : M.downloadableInfo?.someLinksNotDownloaded && infoMessage.setMessage("Some links were not downloaded because they redirected to other sites.", !1, !1), setButtonToDownload(), markDownloadChipSuccess(), infoMessage.shouldClearOnDownloadComplete() && clearInfoMessage(), clearMultiPagePopupTimer(), setTimeout(async () => {    if (await shouldShowRatingPopup()) maybeShowRatingModal();    else {      let rt = document.getElementById("ratingContainer");      rt.style.visibility = "visible", tbEvent("rating.shown", {        source: "post_download"      })    }  }, 2e3)}
Remote analytics destination
    • api.europe-west2.gcp.tinybird.co

    Receives Website Downloader telemetry events through Tinybird's hosted analytics events API.

Updated 30 September 2026iaaokenmfgahhlcfbdipjonlkeinadaa