Are extensions allowed to exfiltrate AI chats, unrelated to their core function?

187 views
Skip to first unread message

James Arnott

unread,
Jun 14, 2026, 1:24:44 PMJun 14
to Chromium Extensions
Hi,

I was just wondering if extensions are allowed to exfiltrate user AI chats, unrelated to their core features, without the user being explicitly aware when they're using these AI chat platforms.

As far as I know, it's against the rules, but when I report them, nothing happens or they just get their featured badges taken away.

I made a blog post about this here.
Here's the list of extensions I caught exfiltrating AI chats (they also all exfil URLs, all but StayFocusd + StayFree exfil full URLs with no exceptions from what I see).

CleanShot 2026-06-14 at 18.09.09@2x.png

This list includes a list of "confirmed" and "capability", where confirmed means I saw it exfiltrating AI chats with my own eyes and capability means that AI chat collection can be enabled via remote config. Poperblocker, for example, started collecting AI chats ~24 hours after installing it, enabled by the remote config.

I'm speaking at DEF CON 34, later this year about extensions, and I'd love to say "Google cares about your privacy".

Here's some demo videos of the extensions exfiltrating this data:

Similarweb - hoklmmgfnpapgjgcpechhaamimifchmp - https://www.youtube.com/watch?v=-c5Jewuqrqw
Poper Blocker - bkkbcggnhapdmkeljlodobbkopceiche - https://www.youtube.com/watch?v=jtExgNjBGMo
WhatRuns - cmkdbmfndkfgebldhnkbfhlneefdaaip - https://www.youtube.com/watch?v=UYwUmaVohQk
StayFocusd - laankejkbhbdhmipfmgcngdelahlfoji - https://www.youtube.com/watch?v=IOdGJEky1SU
(stayfocusd AI chat exfil capability is demonstrated here by swapping out the remote server, I have not seen them exfiltrating AI chats with my own eyes yet)

I've not made a demo video for Stylish but I have manually confirmed the exfiltration of AI chats, via the remote config. ID: fjnbnpbmkenffdnngjfgmeleoegfcffe blog post detailing the obfuscation + url exfil: https://amibeingpwned.com/blog/stylish-is-back-back-again

I'd appreciate some kind of action taken here. I know UrbanVPN got caught exfiltrating AI chats and they got taken off the chrome web store, so I don't see any reason these extensions, confirmed to be exfiltrating AI chats (which does not align with their stated purpose) would be allowed to do the same.


James Arnott

unread,
Jun 15, 2026, 4:27:30 PMJun 15
to Chromium Extensions, James Arnott
Just to update this quick, WhatRuns has stopped collecting AI chats and has since disabled the functionality so it cannot be remotely enabled, they are still however sending off full URLs of every page the user visits, they also send off only the hostname of the site the user is visiting in a different request to get the data for the functionality.

I've rechecked Poper Blocker (v8.7.1), Stylish (v3.4.15) and Similarweb and they're all still exfiltrating AI chats currently.

To deobfuscate the requests from "Poper blocker", copy & paste the request outgoing contents into this function (working on v8.7.1):

 function decodePoper(s) {
    const rot47 = x => x.replace(/[!-~]/g, c => String.fromCharCode(33 + (c.charCodeAt(0) - 33 + 47) % 94));
    const deep = v => {
      if (typeof v === 'string') {
        let t = v;
        try { const u = decodeURIComponent(t); if (u !== t) t = u; } catch {}
        if (/^[\[{]/.test(t)) { try { return deep(JSON.parse(t)); } catch {} }
        return t;
      }
      if (Array.isArray(v)) return v.map(deep);
      if (v && typeof v === 'object') return Object.fromEntries(Object.entries(v).map(([k,x]) => [k, deep(x)]));
      return v;
    };
    let out;
    try { out = deep(JSON.parse(rot47(s))); } catch { out = deep(rot47(s)); }

    return out;
  }
I don't want to manually reinstall the poperblocker extension to update the version on the sandbox last time it had a 24 hour delay before it started collecting the chats.

Stylish has been reconfirmed today to still be exfiltrating AI chats.

Here's the deobfuscation script, copy & paste the request outgoing contents into this function (confirmed working on v3.4.15):
async function decodeStylish(blob) {
    const key = await crypto.subtle.importKey("jwk",
      {alg:"A256CBC",ext:true,
k:"MaQ2KBEEiYcOcSCfszxMBVrKsXK3hxGmxZ8Zjq50KZg",
key_ops:["decrypt"],kty:"oct"},
      "AES-CBC",false,["decrypt"]);
    const raw = Uint8Array.from(atob(blob), c => c.charCodeAt(0));
    const dec = await crypto.subtle.decrypt({name:"AES-CBC",iv:raw.slice(0,16)}, key, raw.slice(16));
    const rows = new TextDecoder().decode(dec).split("\n");
    let b64 = "";
    for (let col = 0; col < rows[0].length; col++)
      for (const row of rows) { const ch = row[col]; if (ch && ch !== " ") b64 += ch; }
    const obj = JSON.parse(atob(b64));
    const once = atob(obj.e.replace(/-/g,"+").replace(/_/g,"/"));
    const qs = atob(once.replace(/-/g,"+").replace(/_/g,"/"));
    return Object.fromEntries(new URLSearchParams(qs));
  }



Reply all
Reply to author
Forward
0 new messages