Is FDev Security safe?
FDev Security sends the hostname of every site you visit to its developer's own server, with no way to turn it off.
On every top-level page load, the extension checks the site's hostname against a locally cached allow list and, if it isn't there, sends that hostname to lenhatthanh.com/api/v1/domain-check.php. The server's response decides whether a warning screen is injected into the page. This check runs unconditionally at startup and, unlike the extension's other blocking features, has no corresponding setting in the options page to disable it.
Who publishes itlenhatthanh20 - no other listings under this identity
lenhatthanh20 - no other listings under this identity
What this publisher told the store about itself, and the other listings that told it the same thing.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
FDev Security reports every website you visit to the developer's own server
Code analysis shows the background service worker sends the hostname of every page you visit to the developer's own domain, lenhatthanh.com, for every new site.
There is no setting to turn off this per-navigation check-in.
What actually happens
You open any top-level webpage, from a search result to your work email.
The background service worker sends that page's hostname to the developer's own server as the page begins loading.
This runs for ordinary, unrelated sites like example.com or wikipedia.org, not just Facebook or Messenger.
What the domain-check request sends and gets back
- Visited hostnameen.wikipedia.org
The domain of the page you just opened, sent for every new top-level navigation.
- Safety verdict{"isSafety": true}
The server's flag telling the extension whether to show you an unsafe-site warning.
The code that does this
The navigation listener and outbound POST, as shipped and deobfuscated
(() => { let e = !0; var t; t = async t => { var o; e = !0; const c = null !== (o = await (async e => { const t = await chrome.storage.sync.get(e); return t[e] ? t[e] : null })("allowList")) && void 0 !== o ? o : [], r = new URL(t.url).hostname; if (!c.some((e => e.includes(r)))) try { await (async t => { var o; const c = await fetch("https://lenhatthanh.com/api/v1/domain-check.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ domain: t }) }), r = await c.json(); e = null === (o = r.isSafety) || void 0 === o || o })(r) } catch (e) { console.warn(e) } }, chrome.webRequest.onSendHeaders.addListener(t, { urls: ["*://*/*"], types: ["main_frame"] }, ["requestHeaders"]), chrome.tabs.onUpdated.addListener(((t, o, c) => { var r; if ("complete" === o.status && !e && c) { const e = null !== (r = c.url) && void 0 !== r ? r : ""; if (!e.startsWith("http://") && !e.startsWith("https://")) return; const o = "./scripts/realtime-protection.content-script.js"; chrome.scripting.executeScript({ target: { tabId: t }, files: [o] }, (() => { if (chrome.runtime.lastError) { const e = `Script injection failed: ${chrome.runtime.lastError.message}`; console.warn(e) } else((e, t) => { chrome.tabs.sendMessage(e, t) })(t, { type: "dangerousSiteDetected", url: e }) })) } })) })()})();Where the hostname goes
- lenhatthanh.com
Receives the hostname of every top-level page you visit and returns a safety verdict used to show or suppress the extension's own warning modal.
Reproduce it yourself
Loads the unpacked extension in Chromium and confirms a domain-check POST fires for ordinary, unrelated sites, not just Facebook or Messenger.
- Node.js 18+
- npm i puppeteer
- Unpacked copy of the extension
// verify_domain_check.js// Loads the extension unpacked in real Chromium and confirms a POST to// the domain-check endpoint fires for ordinary, unrelated sites, not just// Facebook or Messenger.const puppeteer = require('puppeteer');const path = require('path');(async () => { const extensionPath = path.resolve(process.argv[2] || './fdev-security-unpacked'); const browser = await puppeteer.launch({ headless: false, // MV3 service workers need a headed context to load args: [ `--disable-extensions-except=${extensionPath}`, `--load-extension=${extensionPath}`, ], }); const targets = ['https://example.com/', 'https://en.wikipedia.org/wiki/Special:Random']; const seen = []; for (const url of targets) { const page = await browser.newPage(); page.on('request', (req) => { if (req.url().startsWith('https://lenhatthanh.com/api/v1/domain-check.php')) { seen.push({ visited: url, body: req.postData() }); } }); await page.goto(url, { waitUntil: 'networkidle0' }); await new Promise((r) => setTimeout(r, 2000)); await page.close(); } console.log(JSON.stringify(seen, null, 2)); console.log( seen.length === targets.length ? 'CONFIRMED: a domain-check POST fired for every visited site.' : 'NOT CONFIRMED: fewer domain-check POSTs than visited sites.' ); await browser.close();})();- 1Unzip the extension's CRX to ./fdev-security-unpacked.
- 2npm i puppeteer.
- 3node verify_domain_check.js ./fdev-security-unpacked
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.
What it can do
Permissions this extension asks for, as declared in version 2.0.2. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on every site you visit
<all_urls>
Keep running in the background while your browser is open
background
Act on the current tab, but only after you click the extension
activeTab
Block and redirect the requests your browser makes
declarativeNetRequest
Store data in your browser
storage
See the address and title of every tab you have open
tabs
Run its own code inside the pages you visit
scripting
Watch every request your browser makes
webRequest
Where it sends data
Destinations our analysis observed FDev Security 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.
- lenhatthanh.com
FDev Security sends data to lenhatthanh.com. No other extension we have analyzed sends data here.
