Is BeLikeNative: Write English like a native. Paraphrase, Rewrite, Translate safe?

Medium risk

BeLikeNative sends selected text from any website to its AI backend and transmits telemetry including visited domains to the same server.

When a user selects text and triggers a keyboard shortcut or toolbar button, the extension forwards that text to backend.belikenative.com with no domain restrictions — text selected on any page can be sent. The extension also runs a background telemetry system that batches and sends event data every 60 seconds, including the current page's domain, browser platform details, a persistent anonymous identifier, and user subscription and usage metrics.

Autom8 LLCv1.8.9Chrome 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

Telemetry sends page context and usage metrics to BeLikeNative

We observed a POST to backend.belikenative.com carrying telemetry: session, ext/browser version, platform, locale, analytics ID, plan, fix count.

Queued from the content script and worker; can attach the page domain to errors.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You browse websites while BeLikeNative is installed.

The manifest runs the content script on every URL pattern.

The extension did this

The extension queues telemetry that can include page domain context and usage metrics.

Queued events are sent to the BeLikeNative backend in JSON batches.

02EvidenceNETWORK CAPTURE
Captured request
POSThttps://backend.belikenative.com/telemetry/events
03EvidenceFIELD TABLE
Telemetry fields sent or prepared by the extension
FieldValueWhy it matters
Session identifier
6f2b1bb0-5a74-4e3c-b9ab-4f692f80f5c1 (illustrative)Lets separate events from the same browser session be grouped together.
Extension and browser versions
extension 1.8.5; Chrome 148.0.0.0Shows which extension build and Chrome version produced the telemetry.
Platform and language
Linux x86_64; en-USAdds device and locale context to the event stream.
Current page domain
docs.example.com (illustrative)Can show which website was open when an extension error or message failure happened.
Analytics identifier
0000000000000000Lets analytics events stay linked across browsing sessions after installation.
Plan and fix metrics
plan free; fixes 0Shows account tier and usage counters such as the number of corrections made.
04EvidenceTEMPORAL PATTERN
When this fires
Every 1 minute
Flushes when 50 events are queued.

Telemetry is uploaded every minute, and the queue can flush sooner after 10 events are waiting.

05EvidenceCODE COMPARE
The code that does this

Telemetry construction, batching, persistent analytics ID, and page-domain context

What it actually does
Builds a telemetry event with browser, platform, locale, timestamp, and context fieldsjs/background.js
function b(e, t, r) {
  if (!e) throw new Error("type required");
  const n = function() {
      if (i) return i;
      let e = "unknown";
      try {
        e = chrome.runtime.getManifest().version || e
      } catch {}
      return i = {
        v: e,
        c: h(navigator.userAgent),
        p: navigator.platform || "unknown",
        l: navigator.language || "unknown"
      }, i
    }(),
    o = {
      sessionId: s || "pending",
      extensionVersion: n.v,
      chromeVersion: n.c,
      platform: n.p,
      locale: n.l,
      type: e,
      timestamp: (new Date).toISOString(),
      context: {
        ...t,
        entryPoint: t.entryPoint || a
      }
    };
  var c;
  return r instanceof Error && (o.error = {
    name: r.name || "Error",
    message: (r.message || "").slice(0, 500),
    stack: r.stack ? (c = r.stack, c && "string" == typeof c ? c.replace(g, "[JWT]").replace(p, "[EMAIL]").replace(y, "").replace(_, "").slice(0, 2e3) : "") : void 0
  }), o
}
Queues events and flushes when 10 are waitingjs/background.js
function w(e) {
  e && "string" == typeof e.type && (o >= 100 || (n = [...n, e], o += 1, n.length >= 10 && k()))
}
Posts up to 50 queued events to the telemetry endpointjs/background.js
async function k() {
  if (0 === n.length) return;
  if (!await async function() {
      return new Promise(e => {
        try {
          chrome.storage.local.get([r], t => {
            chrome.runtime.lastError ? e(!0) : e(!1 !== t[r])
          })
        } catch {
          e(!0)
        }
      })
    }()) return void(n = []);
  const t = await f(),
    o = n.slice(0, 50);
  n = n.slice(o.length), await async function(t) {
    if (!t || 0 === t.length) return;
    const r = l ? `${e}?dry_run=true` : e,
      n = new AbortController,
      o = setTimeout(() => n.abort(new DOMException("Request timed out", "TimeoutError")), 5e3);
    try {
      const e = await fetch(r, {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          events: t.slice(0, 50)
        }),
        signal: n.signal
      });
      l && await async function(e, t) {
        try {
          const r = await t.json();
          ! function(e) {
            if (0 === e.rejected) console.log("%c[BLN Dry Run] ALL ACCEPTED (%d/%d)", "color: #00b894; font-weight: bold;", e.accepted, e.total);
            else {
              console.warn("%c[BLN Dry Run] REJECTED %d/%d", "color: #d63031; font-weight: bold;", e.rejected, e.total);
              for (const t of e.errors) console.warn("  [%d] %s: %s", t.index, t.field, t.error)
            }
          }(r);
          const n = v(e, r);
          u.push(n), u.length > 50 && (u = u.slice(-50)), await async function() {
            const e = m();
            if (e) try {
              e.set({
                [d]: u
              })
            } catch {
              u = u.slice(-10);
              try {
                e.set({
                  [d]: u
                })
              } catch {}
            }
          }()
        } catch (t) {
          const r = t instanceof Error ? t.message : "unknown";
          u.push(v(e, {
            error: `Response parse failed: ${r}`
          }))
        }
      }(t, e)
    } catch (e) {
      if (l) {
        const t = e instanceof Error ? e.message : "unknown";
        console.error("%c[BLN Dry Run] NETWORK ERROR: %s", "color: #d63031; font-weight: bold; font-size: 13px;", t)
      }
    } finally {
      clearTimeout(o)
    }
  }(o.map(e => "pending" === e.sessionId ? {
    ...e,
    sessionId: t
  } : e))
}
Adds analytics data with persistent identifier and user metricsjs/background.js
function U(e, t) {
  try {
    if (!e || "string" != typeof e || !A.has(e) || !N) return;
    q += 1;
    const r = (new Date).toISOString();
    let n = "unknown";
    try {
      n = chrome.runtime.getManifest().version || n
    } catch {}
    w({
      sessionId: "analytics",
      extensionVersion: n,
      chromeVersion: "n/a",
      platform: M.platform,
      locale: M.lang,
      type: "analytics",
      timestamp: r,
      context: {
        entryPoint: "background"
      },
      analyticsData: {
        type: "analytics",
        anon_id: L || "0000000000000000",
        event: e,
        props: t && "object" == typeof t ? {
          ...t
        } : {},
        user: {
          ...M
        },
        seq: q,
        ts: r
      }
    })
  } catch {}
}
Creates or loads the persistent 16-character analytics identifierjs/background.js
async function(e) {
  if (e && !N) try {
    N = !0, await async function() {
      if (L) return L;
      const e = (await O([I]))[I];
      if ("string" == typeof e && 16 === e.length) return L = e, L;
      let t = "unknown";
      try {
        t = chrome.runtime.id || t
      } catch {}
      const r = await O([D]);
      let n = "string" == typeof r[D] && r[D] ? r[D] : "";
      n || (n = String(Date.now()), await j({
        [D]: n
      }));
      const o = new Uint8Array(await crypto.subtle.digest("SHA-256", (new TextEncoder).encode(t + "_" + n)));
      let a = "";
      for (let e = 0; e < o.length && a.length < 16; e++) a += o[e].toString(16).padStart(2, "0");
      return L = a.slice(0, 16), await j({
        [I]: L
      }), L
    }();
    const t = await O([P]);
    t[P] && "object" == typeof t[P] && (M = C(R, t[P])), x(e)
  } catch {}
}("background")
Adds the current page domain to content-script error telemetryjs/content_script.js
window.addEventListener("error", e => {
  try {
    e.filename && e.filename.includes(chrome.runtime.id) && _(e.error || new Error(e.message), {
      domain: location.hostname
    })
  } catch (e) {}
})

Data recipients

backend.belikenative.com
Updated 17 September 2026gchojmpfpbpmpfgdppfdkpchikbcgabp