Is Email Hunter safe?

Medium risk

Email Hunter is medium risk. Email Hunter ships a hardcoded GA4 secret and sends usage events, including toggling the 'unsafe page' checker, tagged with a client ID generated once and stored permanently. Six such requests hit google-analytics.com. Policy omits detail.…

contacts.to.sendv1.48Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Hardcoded Google Analytics API Secret Tracks You With a Persistent ID

Email Hunter ships a hardcoded GA4 secret and sends usage events, including toggling the 'unsafe page' checker, tagged with a client ID generated once and stored permanently.

Six such requests hit google-analytics.com.

Policy omits detail.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You complete setup, or toggle the extension's 'unsafe page' checker on or off.

The extension also fires an event on its own version-update lifecycle event.

The extension did this

Email Hunter logs the interaction to Google Analytics, tagged with a device ID that stays the same for as long as the extension is installed.

The request uses a Google Analytics API secret that is hardcoded into the extension and shared by every install.

02EvidenceFIELD TABLE
Data sent to Google Analytics with every event
FieldValueWhy it matters
Your persistent install ID
89bd49fc-b554-4b64-826c-6f14383ac30fA random ID stored locally lets Analytics tie every event to the same install long-term.
What you clicked
update_checker_enabledThe specific action you took inside the extension is logged as a named event alongside your persistent ID.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://www.google-analytics.com/mp/collect?measurement_id=G-HTSDC0G4R6&api_secret=<redacted>
Observed during dynamic analysis across 6 captured requests, all carrying the same client_id and firing on the setup-consent click and update-checker toggle events.
Body
{
  "client_id": "89bd49fc-b554-4b64-826c-6f14383ac30f",
  "events": [
    {
      "name": "update_checker_enabled",
      "params": {
        "engagement_time_msec": 100
      }
    }
  ]
}
04EvidenceCODE COMPARE
The code that does this

Hardcoded Google Analytics 4 credentials and persistent client ID

What it actually does
Measurement ID and API secret are literal strings in the POST URLdeobfuscated/googleAnalyticsEvents.js:505-516
return r.engagement_time_msec || (r.engagement_time_msec = 100), t.prev = 6, t.t0 = fetch, t.t1 = "".concat(this.debug ? "https://www.google-analytics.com/debug/mp/collect" : "https://www.google-analytics.com/mp/collect", "?measurement_id=").concat("G-HTSDC0G4R6", "&api_secret=").concat("<redacted>"), t.t2 = JSON, t.next = 12, this.getOrCreateClientId();
case 12:
  return t.t3 = t.sent, t.t4 = [{
    name: e,
    params: r
  }], t.t5 = {
    client_id: t.t3,
    events: t.t4
  }, t.t6 = t.t2.stringify.call(t.t2, t.t5), t.t7 = {
    method: "POST",
    body: t.t6
  }, t.next = 19, (0, t.t0)(t.t1, t.t7);
Client ID is generated once and persisted to local storagedeobfuscated/googleAnalyticsEvents.js:421-440
key: "getOrCreateClientId",
value: (l = a(o().mark((function t() {
  var e, r;
  return o().wrap((function(t) {
    for (;;) switch (t.prev = t.next) {
      case 0:
        return t.next = 2, chrome.storage.local.get("clientId");
      case 2:
        if (e = t.sent, r = e.clientId) {
          t.next = 8;
          break
        }
        return r = self.crypto.randomUUID(), t.next = 8, chrome.storage.local.set({
          clientId: r
        });
      case 8:
        return t.abrupt("return", r);
      case 9:
      case "end":
        return t.stop()
    }
  }), t)
}))), function() {
  return l.apply(this, arguments)
})
05EvidenceTHIRD PARTY LIST
Where the event data ends up
  • www.google-analytics.com

    Google LLC's Measurement Protocol collection endpoint; receives every extension-interaction event tagged with the persistent client ID.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Visited Page URLs Sent to Mellowtel's Hazard-Check Backend

Email Hunter bundles Mellowtel's SafeBrowsing library, active by default when you click "Agree and continue" (checkbox pre-checked).

It sends every visited URL to Mellowtel's backend; the response echoes that URL, confirmed on four sites.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You visit any webpage while the default 'check unsafe pages' setting is active.

No action is needed beyond the one-time 'Agree and continue' click at setup.

The extension did this

The extension sends that page's full URL to Mellowtel's hazard-check backend and gets back a response confirming the URL was received.

This happens automatically on every completed page navigation, not just on suspicious sites.

02EvidenceFIELD TABLE
Data sent on every page visit
FieldValueWhy it matters
The page you're viewing
https://www.reddit.com/r/technologyThe exact URL, not just the domain, of every page you visit is transmitted, including any path it contains.
Your browsing pattern over time
sequential URL stream across a browsing sessionBecause this fires on every navigation, Mellowtel's backend receives a running stream of everywhere you go online while browsing.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://cs.fsdifhj.com/api/v2/hazard/verify
200 OK, JSON response body echoed back the exact URL just visited (observed for reddit.com/r/technology, en.wikipedia.org, amazon.com search, and ebay.com search, a 1:1 match across all 4 pages navigated).
Body
(request body is binary/non-UTF-8 encoded data; not human-readable in the capture)
04EvidenceCODE COMPARE
The code that does this

SafeBrowsing SDK wiring in the background service worker

What it actually does
SDK instantiated with the resolved backend domaindeobfuscated/sw.js:5013-5040
return f = new a.default({
  host: "https://id.".concat(r),
  logging: !!Number("0")
}), l = (0, c.default)("dc`]WmsU[fYYaYbh"), e.t1 = Boolean, e.next = 19, chrome.storage.local.get(l);
case 19:
  return e.t2 = l, e.t3 = e.sent[e.t2], d = (0, e.t1)(e.t3), e.next = 24, f.Ol();
case 24:
  return h = e.sent, e.next = 27, (0, o.mv)({
    fv: (0, c.default)("Zi``sbUj][Uh]cb"),
    Bv: d || Boolean(Number("0")),
    Dv: "".concat((0, c.default)("|NCCwB")).concat(r),
    Ov: Boolean(Number("1")),
    og: Number((0, c.default)("GME")),
    Pg: h,
    kg: Number((0, c.default)("GME")),
    Vg: Number((0, c.default)("F")),
    Lv: "".concat((0, c.default)("|NCC}xB")).concat(r),
    Ev: Boolean(Number("1")),
    hv: 720
  });
case 27:
  p = e.sent, y = new i.SafeBrowsingAPI(p), s.forEach((function(e) {
    return e(y)
  })), globalThis.safeBrowsing = function() {
    return Promise.resolve(y)
  };
Every page-visit callback wired to the SDKdeobfuscated/sw.js:10696-10744
function _() {
  return x.apply(this, arguments)
}

function x() {
  return (x = d(f().mark((function e() {
    var t;
    return f().wrap((function(e) {
      for (;;) switch (e.prev = e.next) {
        case 0:
          return e.next = 2, globalThis.safeBrowsing();
        case 2:
          (t = e.sent).enable(), t.onPageVisited(O);
        case 5:
        case "end":
          return e.stop()
      }
    }), e)
  })))).apply(this, arguments)
}

function O(e) {
  return E.apply(this, arguments)
}

function E() {
  return (E = d(f().mark((function e(t) {
    return f().wrap((function(e) {
      for (;;) switch (e.prev = e.next) {
        case 0:
          if ("UNSAFE" !== t.status) {
            e.next = 3;
            break
          }
          return chrome.tabs.sendMessage(t.tabId, {
            method: "unsafePageNotification"
          }), e.abrupt("return");
        case 3:
          k(t);
        case 4:
        case "end":
          return e.stop()
      }
    }), e)
  })))).apply(this, arguments)
}
05EvidenceTHIRD PARTY LIST
Where the visited URL ends up
  • id.mellowtel.it

    Mellowtel Inc.'s primary SafeBrowsing/hazard-check API domain, referenced directly as the default backend host in the bundled SDK code.

  • cs.fsdifhj.com

    Mellowtel's DNS-TXT-resolved fallback domain, the endpoint actually contacted during our test session; the same vendor's infrastructure under a different current hostname.

06EvidencePLAIN NOTE
What the consent screen discloses

The setup screen's only mention of this feature is a checkbox labeled "Don't search emails on unsafe pages," checked by default. It does not name Mellowtel, describe that page URLs are transmitted to a third-party backend, or link to a privacy policy covering that transmission.

What it can do

Permissions this extension asks for, as declared in version 1.48. 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>

  • Show you desktop notifications

    notifications

  • Store data in your browser

    storage

  • Schedule its own background tasks

    alarms

  • Watch every request your browser makes

    webRequest

  • See every page you navigate to, as you navigate to it

    webNavigation

  • Run its own code inside the pages you visit

    scripting

Updated 30 September 2026mbindhfolmpijhodmgkloeeppmkhpmhc