Is Zapier Agents safe?

Medium risk

Zapier Agents is medium risk. Zapier Agents injects an active-tab script during the browser-context flow, returning page text, title, and URL to the Zapier Agents page. Testing did not observe this without login, but the code path is present in the shipped extension.…

Zapierv1.4.31Chrome Web Store
45Risk

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

Active-tab page text is relayed to Zapier Agents

Zapier Agents injects an active-tab script during the browser-context flow, returning page text, title, and URL to the Zapier Agents page.

Testing did not observe this without login, but the code path is present in the shipped extension.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You use the Zapier Agents side panel while browsing in an active tab.

The flow depends on Zapier authentication and the side panel being open.

The extension did this

The extension reads the active tab's visible page text, title, and URL and returns them to the Zapier Agents page.

The service worker sends getPageContent to active-tab-script.js and forwards the response back through the content-script message path.

02EvidenceFIELD TABLE
Fields returned from the active tab
FieldValueWhy it matters
Active tab URL
https://example.com/account/settingsShows the exact page address open in your active browser tab.
Page title
Account SettingsIdentifies the page or document visible in the browser tab.
Visible page text
Project notes and account details visible on the pageCan include readable content from the active page, including private document, workspace, or account text.
Google document text
Quarterly planning notes from a Google DocWhen the active tab is a Google Doc, the extension can request document content through the Google Docs API.
Google spreadsheet values
This text represents the values in the rows of a spreadsheet: [["Account","Owner"]]When the active tab is a Google Sheet, the extension can request spreadsheet grid values through the Google Sheets API.
03EvidenceNETWORK CAPTURE
Captured request
GEThttps://agents.zapier.com/chrome-extension
Loaded as the Zapier Agents side-panel iframe; no request body is present for this navigation.
04EvidenceCODE COMPARE
The code that does this

The service worker injects the active-tab reader and requests page content

What it actually does
Deobfuscated service-worker.js getBrowserContext handlerservice-worker.js
chrome.runtime.onMessage.addListener(((t, e, n) => {
if ("getBrowserContext" === t) return async function() {
  const t = await vr();
  return new Promise(((e, n) => {
    t.id ? chrome.tabs.sendMessage(t.id, "getPageContent", (function(r) {
      r ? e(r) : (t.url || e(void 0), je(`Unable to get page data from tab: ${t.url} (Last error: ${chrome.runtime.lastError?.message})`), n(new Error(`Unable to get page data from tab: ${t.url}`)))
    })) : n(new Error("No current tab"))
  }))
}().then((t => {
  n(t)
})).catch((() => {
  n("")
})), !0
}))
Deobfuscated service-worker.js active-tab injection hooksservice-worker.js
chrome.tabs.onActivated.addListener((async ({
tabId: t
}) => {
if (t) {
  try {
    await chrome.scripting.executeScript({
      target: {
        tabId: t
      },
      files: ["active-tab-script.js"]
    })
  } catch {}
  chrome.tabs.query({
    active: !0,
    currentWindow: !0
  }, (async ([t]) => {
    Sr(t.url?.includes("linkedin.com") ?? !1)
  }))
}
})), chrome.tabs.onUpdated.addListener(((t, e) => {
(e.url || e.status) && chrome.tabs.query({
  active: !0,
  currentWindow: !0
}, (async ([e]) => {
  try {
    await chrome.scripting.executeScript({
      target: {
        tabId: t
      },
      files: ["active-tab-script.js"]
    })
  } catch {}
  Sr(e.url?.includes("linkedin.com") ?? !1)
}))
}))
05EvidenceCODE COMPARE
The code that does this

The active-tab script returns page text, title, and URL

What it actually does
chrome.runtime.onMessage.addListener((function(t, e, n) {
  return "getPageContent" === t && (A({
    category: "chrome-extension",
    message: `getPageContent from ${window.location.href}`,
    level: "info"
  }), window.location.href.includes("docs.google.com/document") ? (A({
    category: "chrome-extension",
    message: "getPageContent from Google Doc",
    level: "info"
  }), chrome.runtime.sendMessage("getAuthToken", (function(t) {
    if (t) {
      const e = new RegExp(/https:\/\/docs.google.com\/document\/d\/(.*)\//).exec(window.location.href),
        s = e?.[1];
      if (s) {
        const e = {
          method: "GET",
          headers: {
            Authorization: `Bearer ${t}`,
            "Content-Type": "application/json"
          }
        };
        try {
          fetch(`https://docs.googleapis.com/v1/documents/${s}`, e).then((t => t.json())).then((function(t) {
            const e = t.body?.content?.map((t => t.paragraph?.elements)).flat().filter((t => Boolean(t))).map((t => t?.textRun)),
              s = e?.map((t => t?.content)).join("") ?? "";
            n({
              source: "Google Docs",
              text: s,
              title: t.title,
              url: window.location.href
            })
          }))
        } catch (t) {
          j(`Fetch failed for for https://docs.googleapis.com/v1/documents/${s}`)
        }
      } else n({
        source: "Google Docs",
        text: "Unable to get the content of this document",
        title: document.title,
        url: window.location.href
      }), j(`No documentId found for ${window.location.href}`)
    } else n({
      source: "Google Docs",
      text: "Unable to get the content of this document",
      title: document.title,
      url: window.location.href
    }), j(`No auth token found for ${window.location.href}`)
  }))) : window.location.href.includes("docs.google.com/spreadsheets") ? (A({
    category: "chrome-extension",
    message: "getPageContent from Google Sheet",
    level: "info"
  }), chrome.runtime.sendMessage("getAuthToken", (function(t) {
    if (t) {
      const e = new RegExp(/https:\/\/docs.google.com\/spreadsheets\/d\/(.*)\/edit\?gid=(.*)#/).exec(window.location.href),
        s = e?.[1],
        o = e?.[2];
      if (s) {
        const e = {
          method: "GET",
          headers: {
            Authorization: `Bearer ${t}`,
            "Content-Type": "application/json"
          }
        };
        try {
          fetch(`https://sheets.googleapis.com/v4/spreadsheets/${s}?includeGridData=true`, e).then((t => t.json())).then((function(t) {
            const e = t.sheets?.find((t => t.properties?.sheetId === Number(o))),
              s = e?.data?.map((t => t.rowData?.map((t => t.values?.map((t => t.formattedValue)))))).flat(1);
            n({
              source: "Google Sheets",
              text: `This text represents the values in the rows of a spreadsheet: ${JSON.stringify(s)}`,
              title: t.properties?.title ?? document.title,
              url: window.location.href
            })
          }))
        } catch (t) {
          j(`Fetch failed for for https://sheets.googleapis.com/v4/spreadsheets/${s}`)
        }
      } else n({
        source: "Google Sheets",
        text: "Unable to get the content of this spreadsheet",
        title: document.title,
        url: window.location.href
      }), j(`No spreadsheetId found for ${window.location.href}`)
    } else n({
      source: "Google Sheets",
      text: "Unable to get the content of this spreadsheet",
      title: document.title,
      url: window.location.href
    }), j(`No auth token found for ${window.location.href}`)
  }))) : window.location.href.includes("linkedin.com") ? (A({
    category: "chrome-extension",
    message: "getPageContent from LinkedIn",
    level: "info"
  }), n({})) : (A({
    category: "chrome-extension",
    message: "getPageContent from standard webpage",
    level: "info"
  }), n({
    text: document.documentElement.innerText,
    title: document.title,
    url: window.location.href
  }))), !0
}))
06EvidenceCODE COMPARE
The code that does this

The side panel embeds the remote Zapier Agents page

What it actually does
<html><body><head><style>html, body {
        margin: 0;
        padding: 0;
        height: 100%;
      }

      iframe {
        position: fixed;
        inset: 0;
        width: 100%;
        height: 100%;
        border: 0;
      }</style><script defer="defer" src="sidepanel.js"></script></head><iframe src="https://agents.zapier.com/chrome-extension" id="central" allow="clipboard-write"></iframe></body></html>
07EvidenceTHIRD PARTY LIST
Remote services named in the active-tab content path
  • agents.zapier.com

    Zapier Agents iframe and page context that receives browser-context responses.

  • docs.googleapis.com

    Google Docs API endpoint queried when the active tab is a Google Doc and an OAuth token is available.

  • sheets.googleapis.com

    Google Sheets API endpoint queried when the active tab is a Google Sheet and an OAuth token is available.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

Visible tab screenshots relayed to Zapier Agents on request

The extension can capture the visible browser tab as a JPEG when its Zapier Agents page requests a screenshot.

The image is relayed to agents.zapier.com and can include sensitive content visible on the page.

01EvidenceCAUSE EFFECT
What actually happens
You did this

A logged-in Zapier Agents side panel requests a browser screenshot.

The extension did this

The extension captures the visible tab as a JPEG and relays the screenshot response to Zapier Agents.

02EvidenceNETWORK CAPTURE
Captured request
POSThttps://agents.zapier.com
03EvidenceFIELD TABLE
Fields in the request
FieldValueWhy it matters
Visible tab information
Screenshot of the active tab (illustrative)The request can include what is currently visible in your active browser tab, including page content shown on screen.
04EvidencePLAIN NOTE
Observation

Dynamic analysis confirmed the screenshot capture path in the extension code. Earlier testing could not trigger the behavior without Zapier authentication and an open side panel.

Updated 17 September 2026jfcmjbboehfdmgbhheahjlnoimbgfdbn