Is Eclipse Ad Blocker safe?
Eclipse Ad Blocker is medium risk. Eclipse Ad Blocker fetches rules, whitelist, net filters, and CSS config from eclipseadblocker.com. The service worker applies these to blocking rules and page-context scriptlets, with no signature or hash check before applying updates.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Remote Eclipse Ad Blocker config changes page behavior
Eclipse Ad Blocker fetches rules, whitelist, net filters, and CSS config from eclipseadblocker.com.
The service worker applies these to blocking rules and page-context scriptlets, with no signature or hash check before applying updates.
You keep Eclipse Ad Blocker installed long enough for its scheduled update check to run.
The extension fetches remote configuration and applies it to filtering rules, page CSS, and scriptlets.
The confirmed evidence did not show cryptographic verification before those updates were used.
The extension refreshes remote configuration on a recurring schedule after installation.
| Field | Value | Why it matters | |
|---|---|---|---|
Rule list URL | https://eclipseadblocker.com/rules/rules_1.json?userId=ai9WZkg2SXJUU20veDY0QTU5UmhKeUhYM0p2UU1NR082M21ua3ZvNW1SWWszR1JMYmlHY1ZYMUNOdm5rSW5jVnZ2b0taYStB | This tells your browser which additional rule file to download and store for page-specific behavior. | |
Remote version number | 42 | This decides whether the extension treats the server response as newer than what is already stored. | |
Whitelist domains | example.com | These domains can be added to the list where the extension allows requests instead of blocking them. | |
Scriptlet function and arguments | json-prune with server-provided arguments | These values choose which page-context scriptlet runs and what arguments it receives. | |
CSS selector list | popIn_infinite_ad | These selectors decide which page elements the extension hides after a page loads. |
Remote update and scriptlet execution paths
async update() {
const {updatedAt} = await chrome.storage.local.get(['updatedAt']);
const updatePeriodMs = 24 * 3600000;
if (updatedAt && (Date.now() - updatedAt) < updatePeriodMs) {
this.log('Update was perfomed less then 24 hours ago');
return;
}
await Promise.all([this.updateRules(), this.updateWhitelist(), this.updateNetFilters(), this.updateCommonCSS()]);
await chrome.storage.local.set({updatedAt: Date.now()});
}async updateRulesData(url, version) {
const storageKey = 'rule_' + url;
const d = await this.download(url);
d[storageKey] = version;
await chrome.storage.local.set(d);
}
async updateRules() {
const rulesList = await this.download(RULES_URL);
const promises = [];
for (const ruleInfo of rulesList) {
const storageKey = 'rule_' + ruleInfo.url;
const storageInfo = await chrome.storage.local.get([storageKey]);
const storageVersion = storageInfo[storageKey];
if (storageVersion && storageVersion >= ruleInfo.version) {
this.log(ruleInfo.url + ' is updated.');
continue;
}
promises.push(this.updateRulesData(ruleInfo.url, ruleInfo.version))
}
await Promise.all(promises);
}
async updateWhitelist() {
const data = await this.download(WHITE_LIST_URL);
const {localWhiteListVersion} = await chrome.storage.local.get({localWhiteListVersion: 0});
if (localWhiteListVersion >= data.version) {
this.log('whitelist is updated.');
return;
}
const {whiteList} = await chrome.storage.local.get({whiteList: []});
const newWhiteList = [...new Set([...whiteList ,...data.domains])];
await chrome.storage.local.set({localWhiteListVersion: data.version, whiteList: newWhiteList});
await this.updateDynamicNetRules();
}
async updateNetFilters() {
const data = await this.download(NET_FILTERS_URL);
const {localNetListVersion} = await chrome.storage.local.get({localNetListVersion: 0});
if (localNetListVersion >= data.version) {
this.log('Net filters are updated.');
return;
}
const responseNetFilters = await fetch(data.url);
const netFilters = await responseNetFilters.json();
await chrome.storage.local.set({localNetListVersion: data.version, netFilters: netFilters});
await this.updateDynamicNetRules();
}
async updateCommonCSS() {
const data = await this.download(COMMON_CSS_URL);
const {localCommonCssVersion} = await chrome.storage.local.get({localCommonCssVersion: 0});
if (localCommonCssVersion >= data.version) {
this.log('Common CSS is updated.');
return;
}
const commonCSS = await this.download(data.url);
await chrome.storage.local.set({localCommonCssVersion: data.version, commonCSS: commonCSS});
}async runScriptlets(sender, scriptletsList, srcList) {
const functionToInject = (script) => {
const scriptTag = document.createElement('script');
scriptTag.setAttribute('type', 'text/javascript');
scriptTag.textContent = script;
const parent = document.head || document.documentElement;
parent.appendChild(scriptTag);
if (scriptTag.parentNode) {
scriptTag.parentNode.removeChild(scriptTag);
}
};
if (scriptletsList) {
for (const item of scriptletsList) {
try {
let code = scriptlets.invoke({name:item.function, args: item.args})
let tabId = sender.tab.id;
await chrome.scripting.executeScript({
target: {tabId},
world: 'MAIN',
func: functionToInject,
args: [code],
injectImmediately: true
});
} catch (e) {
console.log(e);
}
}
}
if (srcList) {
for (const src of srcList) {
try {
let tabId = sender.tab.id;
await chrome.scripting.executeScript({
target: {tabId},
world: 'MAIN',
func: functionToInject,
args: [src],
injectImmediately: true
});
} catch (e) {
console.log(e);
}
}
}
}
async insertCSS(sender, cssList, excludeCssList) {
const {commonCSS} = await chrome.storage.local.get({commonCSS: []});
let totalList = commonCSS;
if (cssList) {
totalList = [...totalList, ...cssList];
}
if (excludeCssList) {
totalList = totalList.filter(item => excludeCssList.indexOf(item) === -1);
}
const chunkSize = 3000;
for (let i = 0; i < totalList.length; i += chunkSize) {
const chunk = totalList.slice(i, i + chunkSize);
const css = chunk.join(', ') + '{ display: none!important; }';
chrome.scripting.insertCSS({
target: {tabId: sender.tab.id},
css: css
}).catch((error) => {this.log(error)});
}
}
async blockAds(sender) {
if (sender.frameId) {
return null;
}
let domain = new URL(sender.tab.url).hostname;
domain = domain.replace('www.', '').toLowerCase();
if (await this.isInWhiteList(domain)) {
return null;
}
const data = await chrome.storage.local.get([domain]);
const rules = data[domain];
if (!rules) {
return null;
}
this.runScriptlets(sender, rules.scriptlets, rules.src);
this.insertCSS(sender, rules.css, rules.exclude_css);
return {extcss_list: rules.ext_css};
}- eclipseadblocker.com
Serves the rules, whitelist, net filter, common CSS, and rule JSON resources consumed by the extension.
Stable userId sent with Eclipse Ad Blocker updates
Eclipse Ad Blocker stores a stable userId in Chrome sync storage and attaches it to config requests to eclipseadblocker.com.
The same ID appears on whitelist, filter, CSS, and rules-update requests, tying them to one profile.
You install Eclipse Ad Blocker and leave it enabled.
The extension stores a userId and attaches it to its configuration update requests.
The same value was observed on whitelist, net filter, common CSS, and rules requests.
| Field | Value | Why it matters | |
|---|---|---|---|
Your extension userId | ai9WZkg2SXJUU20veDY0QTU5UmhKeUhYM0p2UU1NR082M21ua3ZvNW1SWWszR1JMYmlHY1ZYMUNOdm5rSW5jVnZ2b0taYStB | This value identifies the same browser profile across repeated update checks. | |
Requested update type | /rules/ | The path tells the server which configuration resource your browser is refreshing. | |
Update timing | 2026-07-12T13:39:36Z | Each request also tells the server when your browser is active with the extension installed. |
The identifier is saved in synced extension storage, so the extension can reuse it after the install request.
chrome.storage.sync install data{
"userId": "ai9WZkg2SXJUU20veDY0QTU5UmhKeUhYM0p2UU1NR082M21ua3ZvNW1SWWszR1JMYmlHY1ZYMUNOdm5rSW5jVnZ2b0taYStB",
"delayFinal": 5
}Service worker code that stores and reuses the userId
async download(url) {
const {userId} = await chrome.storage.sync.get(['userId'])
try {
const response = await fetch(url + '?userId=' + userId);
return response.json();
}
catch (e) {
log(e);
}
}chrome.runtime.onInstalled.addListener(async (details) => {
if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
try {
const {userId} = await chrome.storage.sync.get('userId');
if (userId) {
await fetch(START_URL+'?userId='+userId);
chrome.runtime.setUninstallURL(UNINSTALL_URL+'?userId='+userId);
}
else {
const data = await (await fetch(START_URL)).json();
await chrome.storage.sync.set(data);
await chrome.alarms.create(FINAL_ALARM_NAME, {delayInMinutes: data.delayFinal});
chrome.runtime.setUninstallURL(UNINSTALL_URL+'?userId='+data.userId);
await chrome.storage.sync.set({userId});
}
}
finally {
await chrome.storage.local.set({installed: true});
extension.start();
}
}
});chrome.alarms.onAlarm.addListener(async function(alarm) {
if (alarm.name === UPDATE_ALARM_NAME) {
extension.update();
}
else if (alarm.name === FINAL_ALARM_NAME) {
const {userId} = await chrome.storage.sync.get(['userId'])
fetch(FINAL_URL + '?userId=' + userId);
}
});- eclipseadblocker.com
Receives install, final, uninstall, rules, whitelist, net filter, and common CSS update requests with the userId query parameter.
What it can do
Permissions this extension asks for, as declared in version 2.0.1. 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 unknown, which we have not unpacked yet.
Read and change your data on every site you visit
<all_urls>
Schedule its own background tasks
alarms
Run its own code inside the pages you visit
scripting
Store data in your browser
storage
Store an unlimited amount of data in your browser
unlimitedStorage
Block and redirect the requests your browser makes
declarativeNetRequest
See every page you navigate to, as you navigate to it
webNavigation