Is Clean Adblocker safe?

High risk

Clean Adblocker reports every site you visit to its own server and reframes Google/Amazon search pages to scrape and upload the results.

On every page you visit, the extension sends the page URL, the previous URL, and a persistent per-install ID to comet.cleanadblocker.com, gated by a consent check that is always true on Chrome. Separately, when you search on Google it strips the page's anti-framing headers and loads a hidden copy of the search results to extract the query and result titles/URLs/prices, and it reads the same kind of data directly off Amazon search pages, uploading both to the same vendor backend.

75Risk

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

Publishers can request a review.

Findings

SeverityCRITICAL
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI FOUND

Clean Adblocker reports every site you visit, tagged with a permanent ID

Code analysis shows Clean Adblocker sends the URL and referrer of every site you visit to its own server, tagged with a UUID that persists across sessions.

The setting meant to disable this defaults to on for every Chrome install.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You visit any ordinary website, for example a news article or a shopping page.

This is not limited to pages with ads, popups or cookie banners to block.

The extension did this

The extension records that page's address and the page you came from, tags them with a permanent per-install ID, and queues them for upload to its own server.

The upload happens automatically, with no visible prompt or indicator on the page.

02EvidenceCODE COMPARE
The code that does this

consent-gate.js: the switch meant to gate this collection

What it actually does
"use strict";

class ConsentGate {
  container = new DataContainer('dataProcessingConsent', {
    hasConsent: "chrome" !== 'firefox'
  });
  async getConsent() {
    const data = await this.container.get();
    return data.hasConsent;
  }
  async setConsent(hasConsent) {
    await this.container.set({
      hasConsent
    });
  }
}
const dataProcessingConsent = new ConsentGate();
03EvidenceCODE COMPARE
The code that does this

page-data-store.js and tab.js: which pages get reported, and how

What it actually does
page-data-store.js: create() treats any http(s) page as reportablebackground/components/page-data-store.js
async create(url) {
  const isValidSite = url.startsWith('http');
  const host = getUrlHost(url);
  const settings = await userData.getSettings();
  const shouldWorkOnThisSite = isValidSite && settings.enabled;
  let enabled = shouldWorkOnThisSite;
  let hideCookieBanners = shouldWorkOnThisSite && !!settings.hideCookieBanners;
  let blockPopups = shouldWorkOnThisSite && settings.blockPopups;
  let showBlockedPopupNotification = blockPopups;
  if (shouldWorkOnThisSite) {
    const [isHostDeactivated, isPopupsAllowed, isPopupShowNotification, isCookieBannersAllowed] = await Promise.all([deactivatedSites.isHostDeactivated(host), popupAllowedSites.isAllowed(host), popupShowNotificationList.getValueByHost(host), cookieBannersAllowedSites.isAllowed(host)]);
    enabled = enabled && !isHostDeactivated;
    blockPopups = blockPopups && !isPopupsAllowed;
    showBlockedPopupNotification = blockPopups && isPopupShowNotification !== false;
    hideCookieBanners = hideCookieBanners && !isCookieBannersAllowed;
  }
  return {
    pageUrl: url,
    hostAddress: host,
    enabled,
    hideCookieBanners,
    blockPopups,
    showBlockedPopupNotification,
    isValidSite,
    blockTracking: settings.blockTracking,
    previousUrl: '',
    clickId: crypto.randomUUID(),
    stats: {
      total: 0,
      ads: 0,
      trackers: 0,
      popups: 0,
      cookieBanners: 0,
      timeSaved: 0
    }
  };
}
tab.js: onUpdated() queues the report for every isValidSite pagebackground/components/tab.js
const [pageData, tabData] = await Promise.all([pageDataStore.create(url), this.container.get()]);
tabData.lastCapturedSerpUrlByTab ??= {};
pageData.previousUrl = existingPageData?.pageUrl || tabData.tabOpenInitiators[tabId]?.url || '';
setTimeout(async () => {
  if (pageData.isValidSite) {
    await diagnosticsReporter.addReportsBulk([{
      loadTime: new Date().getTime(),
      previousUrl: pageData.previousUrl,
      pageUrl: pageData.pageUrl,
      clickId: pageData.clickId || undefined,
      trt: tabData.transitions[tabId]?.[url]?.trt,
      trq: tabData.transitions[tabId]?.[url]?.trq
    }]);
  }
  await this.clearData(tabId, url);
}, 100);
await pageDataStore.setData(tabId, pageData);
await this.onActivated();
04EvidenceFIELD TABLE
Fields in each report POSTed to comet.cleanadblocker.com/convert
FieldValueWhy it matters
Permanent device ID
a1b2c3d4-5e6f-47a8-9b1c-2d3e4f5a6b7cA random ID created on first install and reused on every report, letting the vendor link all your visits together.
Page you visited
https://www.nytimes.com/2026/09/12/technology/ai-chips.htmlThe full address of the page you just loaded.
Page you came from
https://www.google.com/search?q=latest+ai+chip+newsThe address of the page you were on right before, exposing your browsing path.
Country code
USYour approximate location, derived from your IP address.
Navigation type
linkHow you arrived at the page: typed, clicked a link, or redirected.
05EvidenceTHIRD PARTY LIST
Where every visit report goes
  • comet.cleanadblocker.com

    The vendor's own backend for Clean Adblocker. Receives the browsing report described above at /convert.

06EvidencePLAIN NOTE
Observation

Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI FOUND

Clean Adblocker strips Google's anti-framing headers to copy your searches

Code analysis shows Clean Adblocker removes Google's X-Frame-Options and CSP headers to reload your search in an off-screen iframe, then extracts and uploads your query and results.

Amazon product searches are extracted the same way.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You run a search on google.com, or search for a product on amazon.com.

No click on a result is needed; running the search is enough.

The extension did this

The extension reloads the same search page in an off-screen background frame, reads the query and every result shown, and uploads them to its own server.

On Google, it first strips the page's anti-framing headers so the off-screen copy is allowed to load at all.

02EvidenceCODE COMPARE
The code that does this

renderer.js: stripping Google's frame protection, then reloading the page off-screen

What it actually does
createFrameHeaderRule(): the session rule that removes the protectionbackground/components/serp/renderer.js
createFrameHeaderRule(domain) {
  return {
    id: DNR_RULE_ID,
    priority: 1,
    condition: {
      requestDomains: [domain],
      resourceTypes: ['sub_frame']
    },
    action: {
      type: 'modifyHeaders',
      responseHeaders: [{
        header: 'X-Frame-Options',
        operation: 'remove'
      }, {
        header: 'Frame-Options',
        operation: 'remove'
      }, {
        header: 'Content-Security-Policy',
        operation: 'remove'
      }, {
        header: 'Content-Security-Policy-Report-Only',
        operation: 'remove'
      }]
    }
  };
}
render(): enables the rule, loads the off-screen iframe, then cleans upbackground/components/serp/renderer.js
async render(url, localeParams = {}, timeoutMs = 25000) {
  if (!this.isSupported()) return null;
  const domain = new URL(url).hostname;
  const localizedUrl = this.applyLocaleParams(url, localeParams);
  try {
    await this.toggleDNRRule(domain, true);
    await this.toggleContentScript(domain, true);
    await this.ensureOffscreenDocument();
    const result = await chrome.runtime.sendMessage({
      target: 'offscreen',
      type: 'RENDER_SERP_IN_IFRAME',
      data: {
        url: localizedUrl,
        timeout: timeoutMs
      }
    });
    if (result?.success) {
      return {
        ...result.data,
        engine: 'google'
      };
    }
    remoteLogger.logError(new Error(result?.error || 'Unknown extraction error'), 'SerpRenderer.render');
    return null;
  } catch (error) {
    remoteLogger.logError(error, 'SerpRenderer.render');
    return null;
  } finally {
    await this.toggleContentScript(domain, false).catch(() => {});
    await this.toggleDNRRule(domain, false).catch(() => {});
  }
}
03EvidenceFIELD TABLE
Data extracted per search and uploaded to comet.cleanadblocker.com/update
FieldValueWhy it matters
Search query text
best noise cancelling headphones under 200The exact words you typed into the search box.
Result titles and links
Sony WH-1000XM5, amazon.com/dp/B09XS7JWHHThe title and destination link of every organic and sponsored result shown to you.
Amazon product data
$29.99, 4.6 stars, SponsoredProduct titles, prices, ratings and Sponsored or Amazon's Choice labels from your Amazon results.
Search session marker
6f1a2e9d-88c4-4b0a-9e21-3a7c5d2f9b41A per-search click ID letting the vendor tie this search to other reports about you.
04EvidenceTHIRD PARTY LIST
Where the extracted search data goes
  • comet.cleanadblocker.com

    The vendor's own backend for Clean Adblocker. Receives extracted Google and Amazon search-result data at /update.

05EvidenceARTIFACT
Check if you're affected

Scans an unpacked extension's JS for declarativeNetRequest rules that remove X-Frame-Options or CSP response headers, and prints the target domain.

RequiresNode.js 14+
check_frame_header_strip.js · js
#!/usr/bin/env node
// check_frame_header_strip.js
// Usage: node check_frame_header_strip.js /path/to/unpacked
const fs = require('fs');
const path = require('path');

const root = process.argv[2];
if (!root) {
  console.error('Usage: node check_frame_header_strip.js <path-to-unpacked-extension>');
  process.exit(1);
}

const TARGET_HEADERS = ['x-frame-options', 'content-security-policy', 'frame-options'];
const hits = [];

function walk(dir) {
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
    const full = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      walk(full);
    } else if (entry.isFile() && entry.name.endsWith('.js')) {
      const text = fs.readFileSync(full, 'utf8');
      if (!/modifyHeaders/.test(text)) continue;
      const lower = text.toLowerCase();
      const matchedHeader = TARGET_HEADERS.find(h => lower.includes(h));
      if (matchedHeader && /operation\s*:\s*['"]remove['"]/.test(text)) {
        const domainMatch = text.match(/requestDomains\s*:\s*\[([^\]]+)\]/);
        hits.push({ file: full, header: matchedHeader, domains: domainMatch ? domainMatch[1] : '(dynamic)' });
      }
    }
  }
}

walk(root);

if (hits.length === 0) {
  console.log('No declarativeNetRequest rules removing framing-protection headers were found.');
  process.exit(0);
}

console.log('Found rules that strip framing-protection headers:');
for (const hit of hits) {
  console.log(`  ${hit.file}\n    header: ${hit.header}\n    domains: ${hit.domains}`);
}
console.log('\nThis pattern lets the extension load a target site in an iframe it opted out of via these headers.');
How to run it
  1. 1
    Unpack the .crx or unzip the extension.
  2. 2
    Run: node check_frame_header_strip.js /path/to/unpacked
  3. 3
    Any match means that domain's anti-framing protection can be defeated.
06EvidencePLAIN NOTE
Observation

Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.

Where it sends data

Destinations our analysis observed Clean Adblocker 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.

  • comet.cleanadblocker.com

    Clean Adblocker sends data to comet.cleanadblocker.com. No other extension we have analysed sends data here.

Updated 30 September 2026belhickmilokebkpefipoaphleineben