Is Privacy Extension for WA Web safe?

Medium risk

Privacy Extension for WhatsApp Web sends WhatsApp page telemetry and a persistent client ID to Google Analytics.

On web.whatsapp.com, the extension injects a page script that hooks WhatsApp events and sends analytics beacons. Those beacons include a localStorage client ID plus page details such as the current URL, referrer, title, language, screen size, WhatsApp version, and login/logout/authenticated events.

waplus-privacyv2.0.28Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

Injected WhatsApp Web page script sends persistent ID to Google Analytics

Opening WhatsApp Web makes the extension's injected page script create a persistent client ID and send it, plus the page URL, referrer and title, to Google Analytics with no consent prompt.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

Opens web.whatsapp.com with the extension enabled.

The extension's content script is already registered on every web.whatsapp.com page.

The extension did this

The injected page script creates a persistent tracking ID and sends it, the page URL, referrer, and title to Google Analytics.

The beacon fires automatically on page load, with no consent prompt in the extension's own UI.

Captured request
POSThttps://analytics.google.com/g/collect?v=2&tid=G-MTQ4KY110F&cid=1765583280.1788897100&dl=https%3A%2F%2Fweb.whatsapp.com%2F&dr=&dt=W%3A%20-%2C%20WA-JS%3A%204.3.0&en=page_view

204 No Content, standard Google Analytics Measurement Protocol acknowledgment with no response body.

Body
en=page_view&_ee=1&ep.authenticated=true&ep.method=multidevice&_et=812
What the beacon contains
  • Persistent client ID
    1765583280.1788897100

    A unique ID stored in your browser that links every WhatsApp Web visit to the same identity over time.

  • Page address
    https://web.whatsapp.com/

    The full WhatsApp Web URL open when the beacon fires.

  • Page title and script version
    W: -, WA-JS: 4.3.0

    The tab title, rewritten by the script to include its bundled library version.

  • Login status
    authenticated=true

    Whether you are currently signed in to WhatsApp Web in this browser session.

  • Event name
    en=page_view

    Which action triggered the beacon: page view, login, logout, or ongoing engagement.

The code that does this

The bundled Tracker class builds the persistent client ID and beacons it via sendBeacon

Readable version

Client ID creation, header fields, and processEvents()

inject/inject.js
class Tracker {  static get clientState() {    const isFirstVisit = !localStorage.cid;    const clientId = localStorage.cid || sessionStorage.cid      || randomInt(1e9) + "." + Math.floor(Date.now() / 1000);    localStorage.cid = sessionStorage.cid = clientId;    return { firstVisit: isFirstVisit, cid: clientId };  }  constructor(trackingId) {    this.trackingId = trackingId;    this.events = [];    this.userProperties = {};    this.lastTime = Date.now();    this.hitsCount = 1;    this.documentTitle = "";  }  getHeader() {    const { cid, firstVisit } = Tracker.clientState;    return {      v: 2,      tid: this.trackingId,        // Google Analytics measurement ID      cid,                         // the persistent client ID      _fv: firstVisit ? 1 : undefined,      ul: (navigator.language || "").toLowerCase() || undefined,      sr: `${screen.width}x${screen.height}`,      dl: location.href,           // full page URL      dr: document.referrer,      dt: this.documentTitle || document.title    };  }  processEvents() {    const events = this.events;    if (this.events = [], !events.length) return;    const eventParams = events.map(([name, props, elapsed]) => {      const pairs = [["en", name], ["_ee", "1"]];      for (const key in props) pairs.push([`ep.${key}`, String(props[key])]);      pairs.push(["_et", String(elapsed)]);      return pairs;    });    const headerParams = Object.entries(this.getHeader())      .filter(([, v]) => v !== undefined)      .map(([k, v]) => [k, String(v)]);    const query = new URLSearchParams(headerParams);    // Beacon fires with no user interaction and no consent prompt.    navigator.sendBeacon(`${Tracker.collectURL}?${query}`);  }}Tracker.collectURL = "https://analytics.google.com/g/collect";

A Tracker is created hardcoded to the extension's own GA property on page load

inject/inject.js
export const waVersion = "4.3.0";// One Tracker per WhatsApp page load, hardcoded to this extension's// own Google Analytics property, gated only by an internal config flag.const titleParts = ["W: ", "-", ", WA-JS: ", waVersion];const primaryTracker = new Tracker("G-MTQ4KY110F");const secondaryTracker = config.googleAnalyticsId  ? new Tracker(config.googleAnalyticsId)  : null;internalEvents.on("loader.injected", () => {  const authenticated = isAuthenticated();  const method = isMultiDevice() ? "multidevice" : "legacy";  if (!config.disableGoogleAnalytics) {    primaryTracker.documentTitle = titleParts.join("");    primaryTracker.setUserProperty("method", method);    primaryTracker.setUserProperty("wa_js", waVersion);    primaryTracker.setUserProperty("powered_by", config.poweredBy || "-");  }  if (secondaryTracker && !config.disableGoogleAnalytics) {    primaryTracker.trackEvent("page_view", { authenticated, method });  }});
Where the beacon lands
    • analytics.google.com

    Google's Measurement Protocol endpoint. Receives the client ID, page URL, referrer, and title on every WhatsApp Web page load.

Where it sends data

Destinations our analysis observed Privacy Extension for WA Web 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.

  • analytics.google.comwidely used

    Privacy Extension for WA Web sends data to analytics.google.com. A widely used service: 25 other extensions we have analysed send data here.

Updated 30 September 2026okglcjoemdnmmnodbllbcfaebeedddod