Is Sidebar: ChatGPT, Bookmarks, GPT-4o | Meomni safe?

High risk

Meomni Sidebar sends full page text, selected text, and Gmail thread content to its backend server on demand.

When users interact with the AI assistant, the extension reads the active tab's full page text (title, URL, and extracted body content) and any selected text, then transmits this data to v2-mlqv2syt5a-uc.a.run.app as AI prompt context. The extension also scrapes Gmail inbox metadata via Atom feed and extracts full Gmail conversation threads — including message bodies, sender emails, and attachment names — which are included in chat requests to the same backend. Additionally, it strips Content-Security-Policy and X-Frame-Options headers from all sub-frame requests across every site the user visits.

Rendomv5.0.34Chrome 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-359
SourceAI SANDBOX

Active Page Text Sent as AI Context

Code analysis shows using the extension's Web AI actions collects the active tab's URL, page title, and page text, posted to the Meomni AI backend.

Traffic testing reached the sign-in wall; no live /api/v2/ai/chat body was captured.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You trigger a Web AI action in the extension.

The overlay code configures Web actions with active-page context before calling the AI workflow.

The extension did this

The extension reads the current page and prepares it for an AI chat request.

The service worker asks the content script for page HTML, extracts readable text, and carries the URL and title into the prompt context.

02EvidenceFIELD TABLE
Fields assembled for the AI context
FieldValueWhy it matters
Current page URL
https://company.example/wiki/2026-product-plan (illustrative)Identifies the exact page you asked the extension to summarize or process.
Page title
2026 Product Plan - Internal Wiki (illustrative)Adds a readable label for the page you are viewing.
Extracted page text
Launch goals, customer notes, and draft timeline for Q3 planning (illustrative)Can include the article, document, ticket, or form text visible in the active page.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://v2-mlqv2syt5a-uc.a.run.app/api/v2/ai/chat
Traffic testing reached a sign-in wall before the AI chat path ran; no request body was captured.
Headers
x-app-idfnfdomooadjpfohbepiaonnbdmkdjiog
x-app-version5.0.27
04EvidenceCODE COMPARE
The code that does this

Shipped code path from Web AI action to page-text POST

What it actually does
Web actions attach active-tab context before calling the AI workflowassets/useOverlay-EAeRTiwI.js
h = a(() => {
  if (!r.value?.startsWith("https")) return null;
  let t = {
    author: "system",
    title: "Web",
    templates: ["summarize", "tldr"],
    userContext: [{
      type: "active-tab-content",
      value: ""
    }]
  };
  const e = l(o.activeTab?.url, y.value);
  return e && (t = e), t.templates = t.templates.map(t => ({
    id: t,
    ...o.ai_getTemplate(t).meta || {}
  })), t
});
return {
  isActionLoading: i,
  pageAction: async function(t, e) {
    try {
      if (i.value = t, !o.isTruePlus) return void o.dispatch("actionOpenGetPlusModal");
      o.dispatch("frontAction_navigate", {
        routePreset: "gpt"
      });
      await o.dispatch("actionCallLlm", {
        templateId: t,
        ...e?.userContext && {
          userContext: e.userContext
        },
        save: !0,
        options: {
          activeModel: "advanced"
        }
      })
    } catch (a) {} finally {
      i.value = null
    }
  },
  activeHostRule: h,
  overlayAiTemplates: v,
  overlayApps: m,
  allApps: p,
  allAiTemplates: d,
  updateOverlay: function(t) {
    o.dispatch("commitAndSave", {
      commit: "overlay$merge",
      payload: t,
      to: "overlay"
    })
  }
}
The content script returns the page HTMLcontentScript/contentScript.js
case "getRawHtml":
  return Promise.resolve(document.documentElement.outerHTML);
The service worker parses the active tab and returns title, URL, and textContentassets/background.js--ICHH-ml.js
actionGetActiveTabReader: async (t, e) => {
  const n = await l();
  let r = {
    url: n?.url,
    textContent: "",
    title: n?.title || "",
    excerpt: "",
    length: 0,
    lang: "",
    content: ""
  };
  if (!n?.id || !n?.url || !n.url.startsWith("http")) return r;
  const i = await u(n.id, {
    to: "contentScript",
    name: "getRawHtml"
  });
  if (!i) return r;
  const a = await t.dispatch("offScreenAction", {
    name: "readability-parse",
    data: {
      html: i,
      url: n.url
    }
  }).catch(t => null);
  if (!a) return r;
  let s = a.textContent || "";
  return s && (s = lt(s)), {
    ...a,
    url: n.url,
    textContent: s,
    title: a.title || n.title || "",
    excerpt: a.excerpt || "",
    length: a.length || 0,
    lang: a.lang || "",
    content: a.content || ""
  }
}
ACTIVE_TAB_CONTENT is copied into the prompt context fieldsassets/background.js--ICHH-ml.js
getVariableResult: async (t, e) => {
  const {
    type: n,
    value: r
  } = e;
  let i = {
    type: n,
    content: r
  };
  switch (n) {
    case it.TemplateTypes.VariableType.SELECTED_TEXT: {
      const e = await t.selectedText.value || "";
      i.content = e;
      break
    }
    case it.TemplateTypes.VariableType.ACTIVE_TAB_CONTENT: {
      const e = await t.dispatch("actionGetActiveTabReader");
      if (!e?.textContent) throw new Error("Web page text content is not available");
      e?.title && (i.title = e.title), e?.url && (i.url = e.url), e?.textContent && (i.content = e?.textContent);
      break
    }
    case it.TemplateTypes.VariableType.YOUTUBE_CAPTIONS: {
      const e = await t.dispatch("actionYoutubeGetCaptions");
      i.content = JSON.stringify(e[r || "text"]), i.url = t.activeTab.url || "", i.title = t.activeTab.title || "";
      break
    }
    case it.TemplateTypes.VariableType.DIALOG:
      i.content = r || await t.dispatch("scraperGetConversation");
      break;
    case it.TemplateTypes.VariableType.FILE_DOCUMENT:
      i.content = r ? await t.dispatch("frontAction_urlToText", r) : "", e?.filename && (i.filename = e.filename)
  }
  return i
}
The AI client posts the assembled data to ai/chatassets/background.js--ICHH-ml.js
apiClient: {
  call: async (e, n) => {
    try {
      let i;
      return i = t.ai_useApiKey ? await t.dispatch("openaiApiDirect", {
        ...e,
        options: {
          ...e.options,
          activeModel: (r = e.options?.activeModel, {
            standard: "gpt-4o-mini",
            advanced: "gpt-4o",
            pro: "gpt-5"
          } [r] || "gpt-4o-mini")
        }
      }) : await t.dispatch("meomniApi", {
        route: n ? "ai/chat/stream" : "ai/chat",
        method: "POST",
        data: e,
        onStream: n
      }), i?.choices || i?.usage ? et.extractDataFromResponse(i) : i
    } catch (i) {
      throw i
    }
    var r
  }
}
The vendor backend base URL used by meomniApiassets/mainConfig-D0q9gJWx.js
import {
  b as e
} from "./object-T1iCcc7Q.js";
const t = "https://v2-mlqv2syt5a-uc.a.run.app/api/v2",
  a = {
    microsoft: {
      redirectUrl: `${t}/oauth/microsoft/redirect`,
      clientId: "b6906661-2c9c-428e-8323-bd0e81ebf016"
    },
    ticktick: {
      redirectUrl: "https://us-central1-taberium-backend.cloudfunctions.net/main/api/oauth-redirect-ticktick",
      clientId: "FXuG8s0c7CKyMx2KCx"
    },
    todoist: {
      redirectUrl: "https://v2-mlqv2syt5a-uc.a.run.app/api/v2/oauth/todoist/redirect",
      clientId: "3149ceccf5d944379621cb77d9f7e6e1"
    }
  }
05EvidenceTHIRD PARTY LIST
Destination host
  • v2-mlqv2syt5a-uc.a.run.app

    Meomni backend that receives the AI chat request assembled by the extension code.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

Gmail thread contents sent to AI chat endpoint

When the Gmail feature runs, it reads the visible conversation, extracts sender, text, timestamps, and attachment names, then sends that context to the AI endpoint.

DA confirmed the code runs; it didn't capture the request body.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You use the extension's Gmail response feature while viewing a Gmail conversation.

The extension did this

The extension reads the visible Gmail thread and sends the parsed conversation as AI chat context.

02EvidenceFIELD TABLE
Gmail fields parsed from the page before the AI request
FieldValueWhy it matters
Sender name and email
Maya Patel <maya.patel@example.com> (illustrative)This can identify who is participating in the email thread you are viewing.
Message body text
Hi Maya, the Q3 launch deck is attached. Can you review before 3 PM? (illustrative)This can include the private contents of the email messages visible in the thread.
Message timestamp
Jul 12, 2026, 10:42 AM (illustrative)This shows when messages in the thread were sent or received.
Attachment names
Q3-launch-plan.pdf (illustrative)File names can reveal projects, customers, or subjects discussed in the thread.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://v2-mlqv2syt5a-uc.a.run.app/api/v2/ai/chat
04EvidenceCODE COMPARE
The code that does this

The shipped code gates on Gmail, scrapes the thread, and sends it to AI chat

What it actually does
Gmail side-panel trigger and AI context callassets/AiRespond-DJhiZIU6.js
const xe = a(() => ["mail.google.com"].some(e => oe.value?.includes(e)));
async function fe() {
  try {
    ne.value = !0;
    const e = await Y.dispatch("scraperGetConversation");
    if (ie.value = e.conversation, ie.value) {
      const e = ie.value[ie.value.length - 1];
      X.value = e.content;
      const a = await Y.dispatch("actionCallLlm", {
          templateId: "analyzeIncomingMessage",
          save: !1,
          context: [{
            type: "conversation",
            value: JSON.stringify(ie.value)
          }],
          options: {
            activeModel: "advanced"
          }
        }),
        {
          structured: l
        } = a;
      l?.key_points && (re.value = l.key_points, ue.value = l.summary, de.value = l.deep_answers, ce.value = l.general_answers), l?.incoming_language && pe("language", l.incoming_language), ne.value = !1
    }
  } catch (e) {} finally {
    ne.value = !1
  }
}
Gmail DOM extractionassets/background.js--ICHH-ml.js
scraperGetConversation: async (t, e) => {
  const n = await l();
  if (!n?.url) return;
  let r = [];
  return n?.url?.includes("mail.google.com") && (r = await t.dispatch("scraperGetGmailConversation")), {
    conversation: r
  }
},
scraperGetGmailConversation: async (t, e) => {
  const n = await l();
  if (!n?.url) return;
  const r = await Py.querySelector({
    tabId: n.id,
    selector: '[role="list"]',
    method: "html",
    fetchMethod: "tab"
  });
  if (!r) return;
  return function(t) {
    try {
      const e = [".yj6qo.ajU", ".HOEnZb.adL", ".gmail_quote", ".gmail_extra", ".adM", ".ajv", ".h5", ".zFot5d", ".yj6qo", ".adL"],
        n = st(t);
      e.forEach(t => {
        n(t).remove()
      });
      const r = [],
        i = n('[role="listitem"]');
      return i.each((t, e) => {
        const a = {};
        a.isLastMessage = t === i.length - 1;
        const s = n(e).find("span.gD");
        s.length && (a.sender = {
          name: s.attr("name") || s.text().trim() || "",
          email: s.attr("email") || ""
        });
        const o = n(e).find(".a3s.aiL");
        if (o.length) {
          let t = o.text().trim();
          t = t.replace(/\\n/g, "\n"), t = t.replace(/\s*\n\s*/g, "\n"), t = t.replace(/\n{3,}/g, "\n\n"), t = t.replace(/\s{2,}/g, " "), a.content = t
        }
        const c = n(e).find("[title]");
        c.length && (a.timestamp = c.attr("title") || "");
        const l = n(e).find(".aQH");
        l.length && (a.hasAttachments = !0, a.attachmentNames = [], l.each((t, e) => {
          const r = n(e).find(".aVK").text().trim();
          r && a.attachmentNames?.push(r)
        })), (a.content || a.sender && a.sender.email) && r.push(a)
      }), r
    } catch (e) {
      return []
    }
  }(r)
},
AI chat route and backend base URLassets/background.js--ICHH-ml.js and assets/mainConfig-D0q9gJWx.js
apiClient: {
  call: async (e, n) => {
    try {
      let i;
      return i = t.ai_useApiKey ? await t.dispatch("openaiApiDirect", {
        ...e,
        options: {
          ...e.options,
          activeModel: (r = e.options?.activeModel, {
            standard: "gpt-4o-mini",
            advanced: "gpt-4o",
            pro: "gpt-5"
          } [r] || "gpt-4o-mini")
        }
      }) : await t.dispatch("meomniApi", {
        route: n ? "ai/chat/stream" : "ai/chat",
        method: "POST",
        data: e,
        onStream: n
      }), i?.choices || i?.usage ? et.extractDataFromResponse(i) : i
    } catch (i) {
      throw i
    }
    var r
  }
}

const t = "https://v2-mlqv2syt5a-uc.a.run.app/api/v2";
05EvidenceTHIRD PARTY LIST
Remote service that receives the AI chat request
  • v2-mlqv2syt5a-uc.a.run.app

    Receives the extension's AI chat request at /api/v2/ai/chat, including the conversation context assembled from Gmail.

Data recipients

v2-mlqv2syt5a-uc.a.run.app
Updated 17 September 2026fnfdomooadjpfohbepiaonnbdmkdjiog