Is Adblocker Fortify safe?

Critical risk

Adblocker Fortify is critical risk. Adblocker Fortify fetches a config object listing which sites get extra injected scripts. The operator can add or remove target sites anytime, with no visible update. The injected script, block.js, runs inside every targeted page.…

adblockerfortifyv1.0.4Chrome Web Store
100Risk

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

Publishers can request a review.

Findings

SeverityCRITICAL
ClassUNWANTED
TypeUnexpected
CWECWE-829
SourceAI SANDBOX

Server controls which websites receive injected scripts via remote config

Adblocker Fortify fetches a config object listing which sites get extra injected scripts.

The operator can add or remove target sites anytime, with no visible update.

The injected script, block.js, runs inside every targeted page.

01EvidenceCAUSE EFFECT
What actually happens
You did this

The content script loads on every page visit.

contentScript.js runs at document_start on <all_urls> (manifest content_scripts declaration).

The extension did this

The script reads a server-provided configuration and, when the current site matches an operator-specified entry, injects block.js into the page.

function c() creates a script element, sets its src to chrome.runtime.getURL(opts.opts[i][19]), and appends it to document.head. Target sites are specified in opts.opts[i][21], received from the remote config endpoint.

02EvidenceCODE COMPARE
The code that does this

Config sync and script injection (background.js + contentScript.js)

What it actually does
prefManager.sync() — config fetchbackground.js
async sync(token) {
  try {
    // Fetch: https://adblockerfortify.com/opts/?token=<opt>
    const data = await fetch(u.opts + token).then(r => r.json());
    // Store full response object as 'opts' in chrome.storage.local
    await chrome.storage.local.set({ opts: data });
  } catch (e) { throw e; }
}
contentScript.js — per-page injection loopcontentScript.js
function injectScriptForConfig(configArray, index, configRoot) {
  // Create element of server-specified type (e.g. 'script')
  let el = document.createElement(configArray[index][18]);
  // Set src to extension's block.js URL
  el.src = chrome.runtime.getURL(configArray[index][19]);
  document.head.appendChild(el);
}

function processOpts(opts) {
  const targets = opts.opts[7]; // server-specified index list
  if (!Array.isArray(targets)) return;
  targets.forEach(index => {
    const targetSites = opts.opts[index][21]; // hostname(s) for this entry
    if (currentHostnameMatches(targetSites)) {
      injectScriptForConfig(opts.opts, index, opts);
    }
  });
}

// Runs on every page load
chrome.storage.local.get(['opts']).then(store => {
  if (store.opts) processOpts(store);
});
03EvidenceTHIRD PARTY LIST
Remote configuration source
  • adblockerfortify.com

    Developer-controlled server; issues per-install tokens via /new and delivers per-install site injection config via /opts/?token=<opt>.

04EvidencePLAIN NOTE
What block.js does on injected pages

block.js contains an element-picker overlay (for user-triggered ad removal) and reads additional configuration from its own script element's data attributes. The operator's ability to control which sites receive this script via server config means the injection target list can change at any time without a CWS update.

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-829
SourceAI SANDBOX

Ad-blocker fetches and applies network rules from remote server every hour

Adblocker Fortify fetches rules from the developer's server every 60 minutes via a per-install token.

The response applies to Chrome's blocking engine, unvalidated.

The operator can add allow-rules bypassing blocking, without any update.

01EvidenceCAUSE EFFECT
What actually happens
You did this

A Chrome alarm fires 60 minutes after install and every 60 minutes thereafter.

The alarm named 'updateNetRulesAlarm' is registered during installation in NetworkManager.setupUpdateAlarm().

The extension did this

The extension downloads a rule set from the developer's server and replaces all of Chrome's dynamic blocking rules with the server's response.

chrome.declarativeNetRequest.updateDynamicRules() removes all current dynamic rules and applies the server-provided array without any local schema check or size limit.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://adblockerfortify.com/opts/all/?token=a3f8c12d-7b41-4e09-a1dc-82f0e3c94b17
JSON array of declarativeNetRequest rule objects; content varies per server response.
03EvidenceCODE COMPARE
The code that does this

Rule fetch and apply loop (background.js lines 38-66)

What it actually does
async updateNetworkData() {
  const { lastUpdateTimestamp: e } = await chrome.storage.local.get({ lastUpdateTimestamp: 0 });
  if (!(e && Date.now() - e < this.ms)) {
    // Build URL: https://adblockerfortify.com/opts/all/?token=<opt>
    const url = await this.addNetworkParams(this.durl + '/opts/all/');
    const response = await fetch(url);
    const rules = await response.json();
    // Replace ALL dynamic rules with server-supplied set
    await this.updateNetworkItem(rules);
  }
}

async updateNetworkItem(e) {
  // ...
  const existing = await chrome.declarativeNetRequest.getDynamicRules();
  const removeIds = existing.map(r => r.id);
  await chrome.declarativeNetRequest.updateDynamicRules({
    removeRuleIds: removeIds,
    addRules: [...serverRules, ...preservedHighPriorityRules]
  });
}
04EvidenceTHIRD PARTY LIST
Remote rule source
  • adblockerfortify.com

    Developer-controlled server that issues per-install tokens and delivers JSON rule sets applied directly to Chrome's declarativeNetRequest engine.

05EvidenceTEMPORAL PATTERN
When this fires
Every 1 hour

Rule update fires every 60 minutes after install via a Chrome alarm named 'updateNetRulesAlarm'.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Per-install tracking token sent on every config and rule update

On install, Adblocker Fortify gets a unique token from adblockerfortify.com, stored permanently.

Every later request there includes it as a URL parameter.

DA on two installs found different, per-install tokens persisting across restarts.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You install Adblocker Fortify.

The extension did this

The extension fetches a unique token from adblockerfortify.com and stores it permanently.

From that point on, every request the extension makes to its update server includes this token as a query parameter, allowing the server to track that installation across all future update checks.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://adblockerfortify.com/opts/all/?token=ai9WZkg2SXJUU20veDY0QTU5UmhKeUhYM0p2UU1NR0I3bldwa2YwK2tCWkY4c0R3SjhOTnp4Mnd4R3dMdUFjakt1ckNlYnpn
200 OK, returns JSON array of declarativeNetRequest rules applied as dynamic ad-blocking rules
03EvidenceFIELD TABLE
What the token request reveals to the server:
FieldValueWhy it matters
Your installation token
ai9WZkg2SXJUU20veDY0QTU5UmhKeUhYM0p2UU1NR0I3bldwa2YwK2tCWkY4c0R3SjhOTnp4Mnd4R3dMdUFjakt1ckNlYnpnA unique value assigned to your installation. It doesn't change between sessions or updates; every future request carries it.
Request timestamp
2026-04-18T02:47:55ZThe server receives each update check as it happens, building a timeline of when your browser was active with the extension running.
Your IP address
93.184.216.34Each request exposes the IP your browser is using then. Combined with the token, this links your IP history to your install.
04EvidenceCODE COMPARE
The code that does this

The token assignment and attachment logic in background.js

What it actually does
Token fetch on install
// On first install, request a unique token from the server
const response = await fetch('https://adblockerfortify.com/new');
const data = await response.json();
chrome.storage.local.set({
  opt: data.opt,          // store the unique token
  range: data.range ?? 7200
}, () => {
  prefManager.schedule(data.range ?? 7200);
  // token is also embedded in the uninstall URL so the server
  // knows when this specific installation is removed
  chrome.runtime.setUninstallURL(data.del + data.opt);
  updateNetworkData();
});
Token appended to all update requests
// Reads the stored token and appends it to any URL passed in
async function addNetworkParams(url) {
  return url + '?token=' + await getCachedToken();
}

async function getCachedToken() {
  // In-memory cache so storage isn't hit on every request
  if (!this.tokenCache) {
    const result = await chrome.storage.local.get(['opt']);
    this.tokenCache = result.opt;
  }
  return this.tokenCache;
}
Periodic rule update (fires every 60 minutes)
async function updateNetworkData() {
  const { lastUpdateTimestamp } = await chrome.storage.local.get({ lastUpdateTimestamp: 0 });
  // Skip if updated within the last 24 hours (this.ms = 864_000_000 ms)
  if (lastUpdateTimestamp && Date.now() - lastUpdateTimestamp < this.ms) return;

  // Token is appended here — every rule fetch goes out with ?token=<opt>
  const urlWithToken = await this.addNetworkParams('https://adblockerfortify.com/opts/all/');
  const response = await fetch(urlWithToken);
  const rules = await response.json();
  await this.updateNetworkItem(rules);
  await chrome.storage.local.set({ lastUpdateTimestamp: Date.now() });
}
05EvidenceSTORAGE DUMP
What's stored on your device

The per-install token, written on first install, is never cleared; every future request includes it. Two test installs got different tokens.

Locationchrome.storage.local key 'opt'
Contents
ai9WZkg2SXJUU20veDY0QTU5UmhKeUhYM0p2UU1NR0I3bldwa2YwK2tCWkY4c0R3SjhOTnp4Mnd4R3dMdUFjakt1ckNlYnpn
06EvidenceTHIRD PARTY LIST
Where the token is sent:
  • adblockerfortify.com

    The extension's update server. Receives the token on every rule/config request, issues it on install, and gets an uninstall notice via setUninstallURL.

Updated 17 September 2026mhlppgemmcdfjonomcbgfddlgcjnghga