Is Smart Sidebar: Chat GPT, Claude & DeepSeek safe?

High risk

Smart Sidebar is high risk. When you use document chat with a PDF, Word, Excel, or PowerPoint file, the extension extracts page text into a document-wide string and adds it to the AI prompt context, then sends the chat request to Aitopia's endpoint.…

Chat GPT for Chrome with Claude & DeepSeekv2.0.7Chrome Web Store
75Risk

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

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

Document Chat Sends Extracted File Text to Aitopia

When you use document chat with a PDF, Word, Excel, or PowerPoint file, the extension extracts page text into a document-wide string and adds it to the AI prompt context, then sends the chat request to Aitopia's endpoint.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You upload a document and use the extension's document chat feature.

The extension did this

The extension extracts text from the file and includes that text in an AI chat request.

02EvidenceFIELD TABLE
Document fields placed into the chat request context
FieldValueWhy it matters
Extracted page text
Q3 customer renewal plan: Acme Corp, renewal date 2026-08-01, owner Morgan Lee. (illustrative)The text inside the uploaded file can become part of the prompt sent for analysis.
File name context
Quarterly-plan.pdf - Chat with FileThe request context can include the file name associated with the chat session.
File attachment ID
file_9d4e2f7a61b34c2aWhen the upload API returns an ID, the chat request can reference that uploaded file along with the extracted text.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://beta_api.aitopia.ai/ai/send
04EvidenceCODE COMPARE
The code that does this

Document extraction, prompt building, and POST construction

What it actually does
Readable document upload and text extractionaitopia/assets/4150064f4e68a471945d96f4a29e0771.js
      async handleFileUpload(e) {
        const t = e.target.files[0];
        if (!t) return;
        if (!["application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation"].includes(t.type)) {
          this.notify.error(this.__("Please upload a PDF, Word, Excel, or PowerPoint file"));
          return
        }
        try {
          const n = await N.set(t, null, null);
          n && (n._listkey || n.id || n.file_id) ? this.uploadedFileId = n._listkey || n.id || n.file_id : this.uploadedFileId = null
        } catch (n) {
          console.error("Error uploading file via /files/create", n), this.uploadedFileId = null
        }
        await this.cleanupResources(), this.newChat(null, "ai_single", b.SHA1("chat_with_file")), this.isLoading = !0, this.error = null, this.fileName = t.name;
        try {
          console.log("Loading document file..."), this.isConverting = !t.type.includes("pdf");
          const n = 10 * 1024 * 1024;
          if (t.size > n) {
            this.notify.error(this.__("File is too large. Please upload a smaller file."));
            return
          }
          await this.fileProcessor.loadPDF(t), this.isConverting = !1, console.log("Document loaded successfully");
          const i = this.fileProcessor.getPdfDoc();
          if (i) {
            let o = "";
            const p = i.numPages,
              w = Math.ceil(p / 5);
            for (let f = 0; f < w; f++) {
              const k = f * 5 + 1,
                v = Math.min((f + 1) * 5, p);
              for (let u = k; u <= v; u++) {
                const P = await i.getPage(u),
                  s = await P.getTextContent(),
                  x = s.items.map(S => S.str).join(" ");
                o += x + `

`, await P.cleanup(), s.items.length = 0, Object.keys(s).forEach(S => {
                  s[S] = null
                })
              }
              await new Promise(u => setTimeout(u, 0))
            }
            this.pdfContent = o, this.context_data = {
              mode: "chatwithfile",
              fileName: `${this.fileName} - Chat with File`,
              contextData: {
                context: this.pdfContent
              }
            }
          }
          if (this.totalPages = this.fileProcessor.numPages, console.log("Total pages:", this.totalPages), this.totalPages === 0) throw new Error("No pages found in document");
          this.currentPage = 1, this.fileUploaded = !0, await this.renderThumbnailsWithMemoryManagement(), await this.$nextTick(), await this.initializeContainerWithRetry(), await this.getFileAnalysis(), await b.Browser().storage.local.set({
            current_file_name: t.name
          }), console.log("Starting page render..."), await this.renderCurrentPage(), console.log("Page rendered successfully")
        } catch (n) {
          console.error("Error processing file:", n), this.error = n.message || this.__("Error processing file"), await this.cleanupResources()
        } finally {
          console.log("Setting isLoading to false"), this.isLoading = !1, this.isConverting = !1;
          const n = e.target;
          n.value = ""
        }
Readable document analysis prompt builderaitopia/assets/4150064f4e68a471945d96f4a29e0771.js
      async getFileAnalysis() {
        const e = "fileAnalysis";
        this.suggestionsKey = e;
        try {
          const t = await b.Browser().storage.local.get("current_file_name");
          if (this.ai_single && this.ai_single[e] && t && t.current_file_name === this.fileName) return;
          this.isGettingSuggestions = this.isGettingSummary = !0;
          let r = `You are a highly skilled AI assistant for analyzing file content. I need two things:

        1. A summary of the content in maximum 2 very short sentences. Start the summary with ##SUMMARY_START## and end with ##SUMMARY_END##

        2. Three topic-based question suggestions about the content. Each suggestion should focus on different aspects (one positive, one neutral, one negative) to help understand the file content better. Start each suggestion with ##SUGGESTION_START## and end with ##SUGGESTION_END##

        Do not include labels like "positive", "negative", or "neutral" in the output. Keep suggestions concise but informative.
        Respond in the ${this.settings.language_name} language.

        Content to analyze:
        """${this.pdfContent}"""`;
          this.running = !0;
          let n = Object.assign({}, this.emailData);
          n.mode = "fileAnalysis", this.uploadedFileId && (n.file_ids = [this.uploadedFileId]), console.log("Getting file analysis...");
          const i = this.uploadedFileId != null ? [this.uploadedFileId] : void 0;
          await this.aiChat(r, this.settings.ai_model, "ai_single", e, !0, !0, null, !1, n, i), console.log("File analysis completed.")
        } catch (t) {
          console.error("Error in getFileAnalysis:", t), this.notify.error(this.__("Error happened while analyzing file content"))
        } finally {
          this.isGettingSuggestions = this.isGettingSummary = !1, this.running = !1
        }
      },
Readable shared AI chat builderaitopia/assets/4f31556a7f03453726f116c1aad35aa9.js
  async chat(o = "", e = null, t = "ai_chat", i = "__all", n = !1, a = !1, l = null, r = !1, c = {}, f = !1, d = 0, p, h) {
    var te, ie;
    if (this.ai_wait) return;
    typeof this.aborting[i] < "u" && (this.aborting[i] = null);
    let _ = await s.Browser().storage.local.get(["settings", "credit_detail", "expires", "ai_image_url", "ai_chat_ids", "context_data", "selected_agent", t]);
    if (this.chat_selected_agent = s.notUndefined(_, "selected_agent"), await s.Browser().storage.local.set({
        ai_ask: null
      }), s.notUndefined(_, "context_data") && !(c != null && c.mode)) {
      const T = window.location.pathname,
        x = (te = _.context_data) == null ? void 0 : te.mode,
        H = {
          chatwithfile: "chat_with_file.html"
        };
      x && H[x] && T.endsWith(H[x]) && ((ie = _.context_data) != null && ie.contextData && (o = `##HIDDEN_CONTEXT_START##${_.context_data.contextData.context}##HIDDEN_CONTEXT_END##` + o), t = "ai_single", i = s.SHA1("chat_with_file"), n = !1, a = !1)
    }
    this.key_detail = await s.Browser().storage.local.get(["uuid_hopekey", "uuid_search"]), this.settings = s.notUndefined(_, "settings"), e == null && (e = g.ai_default_model);
    let v = this.settings.ai_image_model == e ? "image" : "text";
    e === null && (e = this.settings.ai_model), this.waiting(!0), this.credit_detail = s.notUndefined(_, "credit_detail"), typeof this.ai_all_response[i] > "u" && (this.ai_all_response[i] = {}), typeof this.ai_all_response[i][t] > "u" && (this.ai_all_response[i][t] = []);
    let w = s.notUndefined(_, `${t}`);
    typeof _.expires != null && (this.expires = _.expires);
    let S = {};
    typeof w == "string" ? (S = JSON.parse(w), Object.keys(S).length > 0 && Object.keys(S).forEach(T => {
      typeof this.ai_all_response[T] > "u" && (this.ai_all_response[T] = {}), this.ai_all_response[T][t] = Object.values(S[T])
    })) : (S = w, S != null && (t == "ai_chat" ? this.ai_all_response[i][t] = Object.values(S) : typeof S[i] < "u" && (this.ai_all_response[i][t] = Object.values(S[i])))), t == "ai_single" && n == !0 && r == !1 && typeof this.ai_all_response[i] < "u" && typeof this.ai_all_response[i][t] < "u" && (this.ai_all_response[i][t] = [], Object.keys(this.ai_all_response[i][t]).length > 0), this.uuid_search = s.notUndefined(this.key_detail, "uuid_search"), this.hopekey = s.notUndefined(this.key_detail, "uuid_hopekey"), document.querySelector("#aifnmjmchg-container");
    let y = `${g.api_url}/ai/send`;
    if (this.credit_detail != null) {
      let T = e;
      if (this.settings.web_access && (T = "GPT-4o"), typeof this.credit_detail[T] < "u" && this.credit_detail[T].balance < this.credit_detail[T].cost && this.credit_detail[T].credit != -999 && this.settings.ai_mode !== "api") {
        this.ai_all_response[i][t].forEach((x, H) => {
          x.role == "limit" && delete this.ai_all_response[i][t][H]
        }), this.ai_all_response[i][t] = Object.values(this.ai_all_response[i][t]), this.ai_all_response[i][t].push({
          item: "Usage Limit please upgrade plan.",
          role: "limit",
          model: e
        }), await this.setDataStorage(t, i), await this.waiting(!1);
        return
      }
    }
    let O = this.ai_all_response[i][t],
      b = 0,
      m = !1;
    Object.keys(this.ai_all_response[i][t]).length > 0 && !s.notUndefined(c, "search") && this.ai_all_response[i][t].forEach((T, x) => {
      b++, O != b && this.ai_all_response[i][t][x].loading == !0 && (this.ai_all_response[i][t][x].loading = !1, m = !0), T.role == "limit" && (delete this.ai_all_response[i][t][x], this.ai_all_response[i][t] = Object.values(this.ai_all_response[i][t]), m = !0)
    }), f && (this.is_group_chat = !0, m = !0), m && await this.setDataStorage(t, i);
    let U = "";
    this.count++;
    let B = "user";
    if (v == "image" && (B = "image"), (c == null || typeof c != "object") && (c = {}), (String(o).trim().length > 0 || B == "image") && r == !1 && (c.prompt_mode = a, d == 0)) {
      const T = {
        item: o,
        role: B,
        model: e,
        title: l,
        loading: B == "image",
        extra_data: c
      };
      h && Array.isArray(h) && h.length > 0 && (T.attachments = h), this.ai_all_response[i][t].push(T)
    }
    v != "image" && s.notUndefined(_, "ai_image_url") != null && s.notUndefined(_, "ai_image_url") !== "" && typeof c.search_mode > "u" && (this.settings.web_access = !1, c.ai_image_url = s.notUndefined(_, "ai_image_url"), await s.Browser().storage.local.set({
      ai_image_url: null
    }), await s.Browser().storage.local.remove(["ai_image_url"]));
    let k = s.ChatId(t, i);
    const $ = p && Array.isArray(p) && p.length > 0 ? p : void 0;
    let A = {
      history: this.ai_all_response[i][t],
      text: o,
      model: e,
      stream: v != "image",
      uuid_search: this.uuid_search,
      mode: t,
      prompt_mode: a,
      extra_key: i,
      extra_data: c,
      chat_id: k,
      language_detail: this.settings.language_detail,
      is_continue: r
    };
    $ && $.length > 0 && (A.file_ids = $);
    const L = this.settings.capabilities;
    L && typeof L == "object" && Object.values(L).some(T => T === !0) && (A.settings = L), this.uuid_extra_data = {
      chat_id: A.chat_id,
      history: A.history,
      mode: t
    }, this.settings.ai_key && this.settings.ai_mode == "api" && (A.token = this.settings.ai_key), this.settings.language && (A.lang_code = this.settings.language);
    let q = this.ai_all_response[i][t].length;
    (v == "image" || s.notUndefined(c, "search")) && (q = q - 1), q < 0 && (q = 0);
    let R = "";
    r == !0 && this.ai_all_response[i][t].forEach((T, x) => {
      typeof T.finish_reason < "u" && T.finish_reason == "length" && (T.finish_reason = "", q = x, U = R = T.item)
    }), this.ai_all_response[i][t].forEach((T, x) => {
      typeof T.finish_reason < "u" && T.finish_reason == "length" && (T.finish_reason = "stop")
    }), v != "image" && (this.ai_all_response[i][t][q] = {
      item: R,
      role: "system",
      model: e,
      loading: !0,
      title: l,
      extra_data: c
    }), await this.setDataStorage(t, i), this.aborting[i] = new AbortController;
    let V = document.querySelectorAll("#aifnmjmchg-stop-chat-" + t),
      G = document.querySelectorAll("#aifnmjmchg-stop-chat-" + t + " button");
    G != null && V != null && (V.forEach(T => {
      T.classList.remove("aifnmjmchg-hide"), T.classList.add("aifnmjmchg-show")
    }), this.aborting[i].signal.addEventListener("abort", () => {
      t == "ai_single" ? Object.values(this.ai_all_response[i][t]).length > 0 && this.ai_all_response[i][t].forEach((T, x) => {
        this.ai_all_response[i][t][x].loading = !1, this.ai_all_response[i][t][x].stop = !0
      }) : (this.ai_all_response[i][t][q].loading = !1, this.ai_all_response[i][t][q].stop = !0), this.setDataStorage(t, i), this.waiting(!1)
    }), Object.keys(G).length > 0 && G.forEach(T => {
      T.onclick = x => {
        this.aborting[i].abort(), this.waiting(!1);
        let H = {
          force: 1
        };
        this.is_group_chat && (H.extra_data = this.uuid_extra_data), s.sendMessage("uuid", H), V.forEach(Y => {
          Y.classList.add("aifnmjmchg-hide"), Y.classList.remove("aifnmjmchg-show")
        })
      }
    }));
    let ee = null;
    return await this.setDataStorage(t, i), ["image"].indexOf(v) !== -1 ? await this.imageApi(t, e, i, A, q, V, ee, y, U).finally(async () => await this.waiting(!1)) : s.notUndefined(this.settings, "web_access") == !0 && ["image"].indexOf(e) === -1 && r == !1 && i == "__all" && !s.notUndefined(c, "search") && o.length <= 2048 && a == !1 ? (s.Browser().web_access_mode = t, s.Browser().web_access_extra_key = i, s.sendMessage("GoogleSearchCall", o, "GoogleSearchCallback", "tabs")) : (s.notUndefined(_, "selected_agent._listkey") && !a && (A.extra_data.agent_id = s.notUndefined(_, "selected_agent._listkey")), await this.aiApi(t, e, i, A, q, V, ee, y, U, this).finally(async T => {
      await this.waiting(!1)
    }))
  },
Readable network POST helperaitopia/assets/4f31556a7f03453726f116c1aad35aa9.js
  async aiApi(o, e, t, i, n, a, l, r, c, f = this) {
    let d = 0;
    return s.notUndefined(this.ai_all_response, `${t}`) || (f.ai_all_response[t] = {}), s.notUndefined(this.ai_all_response, `${t}.${o}`) || (f.ai_all_response[t][o] = {}), s.notUndefined(this.ai_all_response, `${t}.${o}.${n}`) || (f.ai_all_response[t][o][n] = {}), (this.hopekey ?? null) === null && (this.key_detail = await s.Browser().storage.local.get(["uuid_hopekey", "uuid_search"]), this.uuid_search = s.notUndefined(this.key_detail, "uuid_search"), this.hopekey = s.notUndefined(this.key_detail, "uuid_hopekey")), fetch(r, {
      headers: {
        accept: "text/plain",
        hopekey: this.hopekey || null
      },
      body: JSON.stringify(i),
      method: "POST",
      signal: this.aborting[t].signal
    }).then(async p => {
      const _ = p.body.getReader();
      let v = async () => _.read().then(async ({
        value: w,
        done: S
      }) => {
        if (await f.streamFormatter(w, S, t, o, n, d, f), d++, !S) return await v()
      }).catch(w => {
        if (f.waiting(!1), w instanceof DOMException && w.name == "AbortError") {
          document.querySelector(".ai_loading") != null && document.querySelector(".ai_loading").classList.remove("ai_loading"), f.ai_all_response[t][o][n]._abort = !0;
          return
        } else w.message == "network error" ? (f.ai_all_response[t][o][n].loading = !1, f.ai_all_response[t][o][n]._error = JSON.stringify(w), console.log(w.message)) : (f.ai_all_response[t][o][n].loading = !1, console.log(w.message, w.toString(), w), console.log(f.ai_all_response[t][o]), console.log(c ?? null), f.ai_all_response[t][o][n]._error = JSON.stringify(w));
        f.setDataStorage(o, t)
      });
      return await v()
    }).then(p => {}).catch(p => {
      this.waiting(!1), s.notUndefined(this.ai_all_response, `${t}.${o}.${n}`) && (this.ai_all_response[t][o][n].loading = !1, this.ai_all_response[t][o][n].loading = !1);
      let h = '<span class="aifnmjmchg-text-red-600">' + s.__("(Internet connection lost. Please try again)") + "</span>";
      a != null && a.forEach(_ => {
        _.classList.add("aifnmjmchg-hide"), _.classList.remove("aifnmjmchg-show")
      }), c.length > 0 && (h = c + `

` + h), s.notUndefined(this.ai_all_response, `${t}.${o}.${n}`) && (this.ai_all_response[t][o][n].item = h, f.ai_all_response[t][o][n]._error = JSON.stringify(p), this.setDataStorage(o, t)), console.log(p)
    })
  },
05EvidenceTHIRD PARTY LIST
External destination used for document chat
  • beta_api.aitopia.ai

    Aitopia API endpoint that receives document chat requests for response generation.

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

Gmail AI Reply Sends Thread Content to Aitopia

When you use AI reply in Gmail, the extension reads the selected message body, quoted history, sender/receiver, and page context.

The confirmed code path passes these into the AI request posted to Aitopia.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You ask the extension to draft an AI reply in Gmail.

The extension did this

The extension reads the active email thread and passes the collected fields into an AI request.

02EvidenceFIELD TABLE
Gmail fields selected by the extension code
FieldValueWhy it matters
Visible message body
Can you review the attached proposal before the 3 PM client call? (illustrative)This can include the text you are reading or replying to in the current Gmail thread.
Quoted thread history
On Tue, Alex Rivera wrote: Please confirm the renewal terms for Acme Corp. (illustrative)Earlier messages in the same conversation can be included with the new reply context.
Sender email address
alex.rivera@example-company.comThe sender address ties the conversation content to a specific person or organization.
Receiver email address
morgan.lee@example-company.comThe receiver address identifies who is participating in the email thread.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://beta_api.aitopia.ai/ai/send
04EvidenceCODE COMPARE
The code that does this

Gmail selectors, collection, and POST construction

What it actually does
Readable Gmail selector configurationaitopia/assets/4f31556a7f03453726f116c1aad35aa9.js
    ai_reply: {
      "mail.google.com": {
        reply_button: ".ams.bkH",
        button: ".amn",
        container: '[role="listitem"]:last-child',
        alternative_container: '[role="listitem"]:last-child',
        edit_container: '[role="listitem"] .editable',
        reply_insert_container: ".Am.Al.editable.LW-avf",
        dot_position: {
          bottom: "25%",
          left: "85%"
        },
        detail_elements: [{
          name: "history",
          selector: ".adL.gmail_quote,.adL .gmail_quote",
          mode: "simple",
          type: "textContent"
        }, {
          name: "sender_email",
          selector: ".gD[email]",
          mode: "attribute",
          type: "email"
        }, {
          name: "sender_name",
          selector: ".gD[name]",
          mode: "attribute",
          type: "name"
        }, {
          name: "receiver_email",
          selector: ".g2[email]",
          mode: "attribute",
          type: "email"
        }, {
          name: "receiver_name",
          selector: ".g2[name]",
          mode: "attribute",
          type: "name"
        }, {
          name: "email_content",
          selector: ".a3s > *",
          mode: "simple",
          type: "innerText"
        }]
      },
Readable thread field collectoraitopia/assets/4d4a6d26cfb6412d1f0cb229686323fc.js
    getData(t = null, e = null, a = !1, l = !1) {
      if (z.ai_reply[window.location.hostname]) {
        const {
          reply_button: o,
          button: s,
          container: u,
          alternative_container: d,
          edit_container: p,
          reply_insert_container: m,
          dot_position: h,
          detail_elements: v
        } = z.ai_reply[window.location.hostname];
        if (l && this.last_data !== null && document.querySelector(p) !== null && document.querySelector("#aifnmjmchg-reply-wrapper-dot") !== null && this.last_data.email_content.length > 3) return this.last_data;
        t.EmailReply.reply_edit_container = null;
        let g = document.querySelector(u);
        if (g == null && d !== null && (g = document.querySelector(d)), g == null) return t.draggableModalToggle("ai-reply-popup", !1), t.draggableModalToggle("ai-reply-voice-settings", !1), this.reply_insert_text = t.EmailReply.reply_insert_text = null, null;
        if (t.EmailReply.keys = this.buildKeys(), this.keys = this.buildKeys(), g.querySelector(s) !== null ? (this.reply_button_container = null, this.reply_button_container = g.querySelector(s)) : document.querySelector(s) !== null && (this.reply_button_container = null, this.reply_button_container = document.querySelector(s)), this.dot_position = h, this.reply_edit_container = null, document.querySelector(p) !== null ? this.reply_edit_container = document.querySelector(p) : e !== null && (this.first_show = !1, t.draggableModalToggle("ai-reply-popup", !1), this.reply_insert_text = t.EmailReply.reply_insert_text = null), document.querySelector(m) !== null && (this.reply_insert_container = document.querySelector(m)), v && g !== null) {
          v.forEach(C => {
            let S = [];
            g.querySelectorAll(C.selector) !== null && (g.querySelectorAll(C.selector).forEach(A => {
              S.push(f.onSettingTypeByValue(C, A))
            }), z.ai_reply_data || (z.ai_reply_data = {}), typeof z.ai_reply_data[f.SHA1(window.location.hash)] > "u" && (z.ai_reply_data[f.SHA1(window.location.hash)] = {}), z.ai_reply_data[f.SHA1(window.location.hash)][C.name] = f.removeEmptyLines(S.join(`
`)))
          });
          let w = z.ai_reply_data[f.SHA1(window.location.hash)];
          return this.last_data && (this.last_data.email_content.length > 0 ? (w == null && (w = this.last_data), w.email_content || (w = this.last_data), w.email_content.length == 0 && (w = this.last_data), z.ai_reply_data[f.SHA1(window.location.hash)] = w) : this.last_data = null), this.last_data = w ?? this.last_data, !this.first_show && e !== null && this.last_data !== null && this.reply_edit_container !== null && (this.first_show = !0), a ? z.ai_reply_data[f.SHA1(window.location.hash)] : z.ai_reply_data
        }
      }
      return null
    },
Readable AI reply senderaitopia/assets/4d4a6d26cfb6412d1f0cb229686323fc.js
    async sendSingleAi(t, e, a, l, o = null) {
      let s = z.reply_prompts[e],
        u = `${window.location.hash}#${a}`;
      if (typeof t.ai_prompts[`id_${s}`] < "u") {
        let d = t.ai_prompts[`id_${s}`],
          p = f.SHA1(u);
        await f.Browser().storage.local.get("delete_key").then(h => {
          let v = [];
          if (typeof h.delete_key < "u" && (v = h.delete_key), typeof v != "object" && (v = []), v.indexOf(p) === -1) return v.push(p), f.Browser().storage.local.set({
            delete_key: v
          })
        }), t.ai_single != null && typeof t.ai_single == "object" && typeof t.ai_single[p] < "u" && (t.ai_single[p] = {});
        let m = Object.assign({}, l);
        return m.mode = e, await He.build(t, u, o ?? l.email_content, z.ai_default_model, d.prompt, null, m, !1, s)
      }
    },
Readable network POST helperaitopia/assets/4f31556a7f03453726f116c1aad35aa9.js
  async aiApi(o, e, t, i, n, a, l, r, c, f = this) {
    let d = 0;
    return s.notUndefined(this.ai_all_response, `${t}`) || (f.ai_all_response[t] = {}), s.notUndefined(this.ai_all_response, `${t}.${o}`) || (f.ai_all_response[t][o] = {}), s.notUndefined(this.ai_all_response, `${t}.${o}.${n}`) || (f.ai_all_response[t][o][n] = {}), (this.hopekey ?? null) === null && (this.key_detail = await s.Browser().storage.local.get(["uuid_hopekey", "uuid_search"]), this.uuid_search = s.notUndefined(this.key_detail, "uuid_search"), this.hopekey = s.notUndefined(this.key_detail, "uuid_hopekey")), fetch(r, {
      headers: {
        accept: "text/plain",
        hopekey: this.hopekey || null
      },
      body: JSON.stringify(i),
      method: "POST",
      signal: this.aborting[t].signal
    }).then(async p => {
      const _ = p.body.getReader();
      let v = async () => _.read().then(async ({
        value: w,
        done: S
      }) => {
        if (await f.streamFormatter(w, S, t, o, n, d, f), d++, !S) return await v()
      }).catch(w => {
        if (f.waiting(!1), w instanceof DOMException && w.name == "AbortError") {
          document.querySelector(".ai_loading") != null && document.querySelector(".ai_loading").classList.remove("ai_loading"), f.ai_all_response[t][o][n]._abort = !0;
          return
        } else w.message == "network error" ? (f.ai_all_response[t][o][n].loading = !1, f.ai_all_response[t][o][n]._error = JSON.stringify(w), console.log(w.message)) : (f.ai_all_response[t][o][n].loading = !1, console.log(w.message, w.toString(), w), console.log(f.ai_all_response[t][o]), console.log(c ?? null), f.ai_all_response[t][o][n]._error = JSON.stringify(w));
        f.setDataStorage(o, t)
      });
      return await v()
    }).then(p => {}).catch(p => {
      this.waiting(!1), s.notUndefined(this.ai_all_response, `${t}.${o}.${n}`) && (this.ai_all_response[t][o][n].loading = !1, this.ai_all_response[t][o][n].loading = !1);
      let h = '<span class="aifnmjmchg-text-red-600">' + s.__("(Internet connection lost. Please try again)") + "</span>";
      a != null && a.forEach(_ => {
        _.classList.add("aifnmjmchg-hide"), _.classList.remove("aifnmjmchg-show")
      }), c.length > 0 && (h = c + `

` + h), s.notUndefined(this.ai_all_response, `${t}.${o}.${n}`) && (this.ai_all_response[t][o][n].item = h, f.ai_all_response[t][o][n]._error = JSON.stringify(p), this.setDataStorage(o, t)), console.log(p)
    })
  },
05EvidenceTHIRD PARTY LIST
External destination used for the AI reply request
  • beta_api.aitopia.ai

    Aitopia API endpoint that receives the AI reply request for response generation.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Extension plants a persistent tracking cookie on its own website

After you sign in, the extension writes a 360-day cookie hopekey (your account's auth UUID) and eref (your extension ID) onto aitopia.ai and chatgptextension.ai.

Any page there can read these, tying browsing to your install with no prompt.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You log into the extension and later browse to its own website, aitopia.ai.

No further action or consent prompt is needed once you're signed in.

The extension did this

The extension writes your account UUID and its own extension ID into cookies on that site.

The cookies are marked Secure and SameSite=None with a 360-day expiry, so they persist and travel with normal browser requests to the site for a year.

02EvidenceFIELD TABLE
Cookies written to .aitopia.ai and .chatgptextension.ai
FieldValueWhy it matters
Your account identifier
hopekey=7bd11760f579475641830f8f7dab666cThe same UUID the extension uses internally to identify your account is exposed to the website as a plain cookie value.
Which exact extension you installed
eref=fnmihdojmnkclgjpcoonokmkhjpjechgThe extension's Chrome Web Store runtime ID, written as a cookie so the site can distinguish this install from other white-label variants.
Extension-installed flag
aifnmjmchg_app=trueA boolean flag confirming this browser has the extension installed and active.
03EvidenceCODE COMPARE
The code that does this

window.setCookie() called from the extension's own bundled page script

What it actually does
Readable equivalent
window.setCookie = (name, value, days, domain = null) => {
  let expiresStr = "";
  if (days) {
    const d = new Date();
    d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
    expiresStr = "; expires=" + d.toUTCString();
  }
  if (domain == null) {
    domain = window.location.host;
    if (window.location.host.split(".").length > 2) {
      domain = "." + window.location.host.split(".").slice(1).join(".");
    }
  }
  document.cookie = `${name}=${value || ""}; ${expiresStr}; path=/; domain=${domain}; secure; SameSite=None`;
};

// Called for each allowed domain (aitopia.ai, chatgptextension.ai):
config.domainAllows.forEach(domain => {
  window.setCookie("hopekey", account.uuidHopekey, 360, "." + domain);
  if (browser.runtime.id !== manualExtensionId && browser.runtime.id) {
    window.setCookie("eref", browser.runtime.id, 360, "." + domain);
  }
  window.setCookie("aifnmjmchg_app", true, 360, "." + domain);
});
04EvidenceTHIRD PARTY LIST
Where the cookie is readable
  • aitopia.ai

    The extension vendor's own marketing/account site. Can read hopekey (your account UUID) and eref (your exact extension build) on every page load for 360 days.

  • chatgptextension.ai

    A second first-party domain operated by the same vendor, receiving the identical cookie set.

+1 more finding not shown

What it can do

Permissions this extension asks for, as declared in version 2.0.0. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 2.0.7, which we have not unpacked yet.

  • Read and change your data on every site you visit

    <all_urls>

  • Store data in your browser

    storage

  • Run its own code inside the pages you visit

    scripting

  • Read information about your displays

    system.display

  • Add items to the right-click menu

    contextMenus

Updated 30 September 2026fnmihdojmnkclgjpcoonokmkhjpjechg