Is VCU - The Co-Browsing Network safe?

Low risk

VCU records the URL, title, and search terms of every page you visit and writes them to a database other logged-in users can read.

On every completed page load, the extension captures the current tab's URL, title, and — for about 90 major sites (Google, WhatsApp, Amazon, Realtor.com, healthgrades.com, and similar) — the contents of specific search or message fields. Once you're signed into a VCU account, this information is written to VCU's Firebase database, keyed by a hash of the URL. Any other logged-in VCU user who visits (or later visits) the same page can read that entry and see your exact URL, page title, and the captured search or message text.

vcu.networkv0.2.9Chrome Web Store
20Risk
Who publishes it

Meshin AB - no other listings under this identity

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
vcu.network
Declared legal entity
Meshin AB

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

Publishers can request a review.

Findings

VCU writes visited URLs and search text to a shared Firebase database

MediumunwantedCWE-359Static analysis

Code analysis shows the extension logs completed page loads on about 90 sites, keeps the matched search or message text, and, once you sign into a VCU account, writes the URL, that text and the page title to Firebase, keyed by a URL hash.

What actually happens

You did this

You sign into a VCU account, then finish loading a page on one of about 90 tracked sites, such as a Healthgrades or Google search.

The same happens on retailers, real-estate sites, legal-directory sites, and chat apps like WhatsApp and Slack.

The extension did this

The extension records the page's URL, the matched search or message text, and its title, then writes them to a shared Firebase database keyed by a hash of the URL.

Any other signed-in VCU user who reaches the same URL can read that entry.

The code that does this

Domain allowlist and query filter deciding what's retained (background.js)

Readable version

Beautified: the same domain->params table

background.js
"abc.com/": ["show"],        "abcnews.go.com/": ["search"],        "alibaba.com/products": ["SearchText"],        "aliexpress.com/wholesale": ["SearchText"],        "amazon.com/": ["field-keywords", "show"],        "apnews.com/": ["search"],        "autotrader.com/": ["search"],        "avvo.com/": ["search"],        "bandcamp.com/": ["track"],        "banggood.com/search": ["q"],        "bbc.co.uk/": ["programme"],        "bbc.com/": ["search"],        "bestbuy.com/site/searchpage": ["query"],        "bing.com/search": ["q"],        "bostonglobe.com/": ["search"],        "bradsdeals.com/search": ["q"],        "cargurus.com/": ["search"],        "cars.com/": ["search"],        "cbs.com/": ["show"],        "cbsnews.com/": ["search"],        "chicagotribune.com/": ["search"],        "cnn.com/": ["search"],        "costco.com/CatalogSearch": ["keyword"],        "coupons.com/": ["q"],        "craigslist.org/search": ["query"],        "cwtv.com/": ["show"],        "dailymotion.com/": ["video"],        "dealcatcher.com/search": ["search", "q"],        "dealhack.com/search": ["q"],        "dealigg.com/search": ["q"],        "dealighted.com/search": ["q"],        "dealmac.com/search": ["q"],        "dealmoon.com/search": ["search", "q"],        "dealnews.com/search": ["search", "q"],        "dealoftheday.com/search": ["q"],        "deals2buy.com/search": ["q"],        "dealsea.com/search": ["q"],        "dealsplus.com/search": ["q"],        "dhgate.com/wholesale": ["search"],        "dhl.com/en/express/tracking.html": ["AWB"],        "discord.com/": ["message"],        "duckduckgo.com/": ["q"],        "ebates.com/search": ["query"],        "ebay.com/": ["_nkw"],        "edmunds.com/": ["search"],        "etsy.com/": ["q"],        "facebook.com/": ["q", "id"],        "fedex.com/bpost": ["q"],        "findlaw.com/": ["search"],        "flickr.com/": ["photo"],        "fox.com/": ["show"],        "foxnews.com/": ["search"],        "gearbest.com/": ["q"],        "google.com/search": ["q", "oq"],        "groupon.com/browse": ["query"],        "hbo.com/": ["show"],        "healthgrades.com/": ["search"],        "homedepot.com/s": ["search"],        "huffpost.com/": ["search"],        "hulu.com/": ["show"],        "iheart.com/": ["station"],        "imdb.com/": ["title"],        "instagram.com/": ["q", "p"],        "kbb.com/": ["search"],        "kik.com/": ["chat"],        "kmart.com/search": ["q"],        "last.fm/": ["track"],        "latimes.com/": ["search"],        "lawyers.com/": ["search"],        "lightinthebox.com/index.php": ["main_page", "q"],        "line.me/": ["chat"],        "linkedin.com/": ["keywords", "article"],        "livingSocial.com/search": ["query"],        "lowes.com/search": ["q"],        "macys.com/shop/featured": ["q"],        "mixcloud.com/": ["track"],        "myspace.com/": ["song"],        "nbc.com/": ["show"],        "nbcnews.com/": ["search"],        "netflix.com/": ["show"],        "newegg.com/Product/ProductList.aspx": ["Submit", "DEPA"],        "nolo.com/": ["search"],        "npr.org/": ["program", "search"],        "nydailynews.com/": ["search"],        "nypost.com/": ["search"],        "nytimes.com/": ["search"],        "officedepot.com/a/browse": ["Ntt"],        "overstock.com/search": ["keywords"],        "pandora.com/": ["station"],        "pinterest.com/": ["q", "pin"],        "radio.com/": ["station"],        "ratemds.com/": ["search"],        "realtor.com/": ["search"],        "reddit.com/": ["q", "comments"],        "redfin.com/": ["search"],        "retailmenot.com/coupons": ["query"],        "reuters.com/": ["search"],        "reverbnation.com/": ["track"],        "samsclub.com/sams/search/searchResults.jsp": ["searchTerm"],        "savings.com/search": ["q"],        "sears.com/search": ["keyword"],        "sfgate.com/": ["search"],        "shopathome.com/search": ["q"],        "shopular.com/search": ["q"],        "showtime.com/": ["show"],        "skype.com/": ["chat"],        "slack.com/": ["message"],        "slickdeals.net/": ["query"],        "snapchat.com/": ["story"],        "soundcloud.com/": ["track"],        "spotify.com/": ["track"],        "staples.com/office/supplies": ["q"],        "target.com/s": ["searchTerm"],        "telegram.com/": ["msg"],        "thekrazycouponlady.com/": ["search"],        "tiktok.com/": ["video"],        "tripadvisor.com/": ["search"],        "truecar.com/": ["search"],        "trulia.com/": ["search"],        "tumblr.com/": ["q", "post"],        "tunein.com/": ["station"],        "twitch.tv/": ["clip"],        "twitter.com/": ["q", "status"],        "ups.com/ups.app": ["q"],        "usatoday.com/": ["search"],        "usps.com/search": ["text"],        "viber.com/": ["chat"],        "vimeo.com/": ["video"],        "vistaprint.com/search": ["q"],        "vitals.com/": ["search"],        "walmart.com/search": ["query"],        "washingtonpost.com/": ["search"],        "wayfair.com/keyword.php": ["keyword"],        "whatsapp.com/": ["message"],        "wikipedia.org/": ["search"],        "wish.com/search": ["q"],        "wsj.com/": ["search"],        "yahoo.com/": ["p"],        "yelp.com/": ["search"],        "youtube.com/watch": ["v", "list", "index"],        "zillow.com/": ["search"],        "zocdoc.com/": ["search"]      }

Beautified: i() drops every query parameter not in the table for that domain

background.js
function i(t) {      const n = new URL(t),        r = n.pathname,        i = new URLSearchParams(n.search),        o = new URLSearchParams,        u = e[n.hostname.split(".").slice(-2).join(".") + n.pathname] || [];      return i.forEach(((t, n) => {        u.includes(n) && o.append(n, t)      })), o.size ? r + "?" + o.toString() : r    }

The code that does this

Tab-update listener and forwarder to the chat iframe (background.js)

Readable version

Beautified: fires on every completed navigation

background.js
chrome.tabs.onUpdated.addListener(((t, n, r) => {      n.status === chrome.tabs.TabStatus.COMPLETE && a(t).then((n => {        n && s(t, r.url, r.title)      }))    }))

Beautified: builds urlInfo and messages the injected chats.html iframe

background.js
function s(t, n, r) {      const e = new URL(n);      chrome.tabs.sendMessage(t, {        m: "showIframe"      }, {        frameId: 0      }, (() => {        chrome.runtime.lastError      })), chrome.tabs.sendMessage(t, {        m: "initNewUrl",        urlInfo: {          url: e.hostname + i(n),          urlShort: i(n),          domain: e.hostname,          title: o(r),          tabId: t        }      }, void 0, (() => {        chrome.runtime.lastError      }))    }

The code that does this

The chat iframe writing the URL and title to Firebase once you're signed in (chats.js)

Readable version

Beautified: setRoomStats and registerOnlineAtLocation

chats.js
setRoomStats({          state: t,          commit: e        }, n) {          if ((0, Wg.debugConsoleLog)("🏷️ ~ setRoomStats ~ payload:", n), !t.user) return;          const r = "/" === n.urlShort,            i = {              url: n.url,              title: n.title || (0, Wg.domainToTitle)(n.domain),              time: Date.now()            },            o = {              url: n.domain,              title: r ? n.title : (0, Wg.domainToTitle)(n.domain),              isHomePageUrl: r,              isRootUrl: !0,              time: Date.now() - 1            };          pF.ref(`roomStats/${n.urlHash}/info`).set(i).then((() => {            e("setWebsiteStatsCache", {              roomId: n.urlHash,              value: {                info: i              }            })          })).catch((() => {})), pF.ref(`roomStats/${n.domainHash}/info`).set(o).then((() => {            e("setWebsiteStatsCache", {              roomId: n.domainHash,              value: {                info: o              }            })          })).catch((() => {}))        },        registerOnlineAtLocation({          state: t,          dispatch: e,          commit: n        }, r) {          (0, Wg.debugConsoleLog)("🏷️ ~ registerOnlineAtLocation ~ payload:", r), n("setTabOnlinePayload", r);          let i = !!t.user;          const o = vF.onAuthStateChanged((t => {            t && !i ? (chrome.runtime.sendMessage({              m: "logged-in"            }), o && o()) : t || (i = !1)          }));          if (!t.user) return;          const a = pF.ref(".info/connected"),            s = function(e, n, r = !1, i = !1) {              (0, Wg.debugConsoleLog)("🏷️ ~ subscribeToOnline ~ ref, url:", e.path, n);              const o = uF(),                s = e.child(o);              a.on("value", (async t => {                if (!0 === t.val()) {                  i || await (0, Wg.delay)(100);                  const t = Date.now();                  await s.onDisconnect().remove(), await s.set(Date.now()), (0, Wg.debugConsoleLog)(`🏷️ Got online for ${n} at ${t}`)                } else await s.remove()              })), chrome.runtime.onMessage.addListener((e => {                ("logging-out" === e.m || ("disconnect" === e.m || "hideTabs" === e.m && !r) && e.instanceId === t.instanceId) && (a.off("value"), s.remove())              }))            },            c = t.user.uid,            l = r.urlHash,            u = r.domainHash,            d = pF.ref(`online/${l}/${c}`),            h = pF.ref(`online/${u}/${c}`),            f = pF.ref(`publicOnline/${c}/${u}`),            p = pF.ref(`publicOnline/${c}/${l}`),            v = pF.ref(`publicOnline/${c}/${zg.on}`);          return chrome.runtime.sendMessage({            m: "foward-runtime-message",            message: {              m: "disconnect",              instanceId: t.instanceId            }          }).then((() => {            t.tabsHidden || (s(d, r.url), s(h, r.domain, !1, !0), "/" !== r.urlShort && s(p, r.url), s(f, r.domain, !1, !0)), s(v, "/", !0, !0), a.on("value", (async t => {              !0 === t.val() && e("setRoomStats", r)            }))          })), !0        }

What gets written to the Firebase record for the page you visited

  • Page URL + matched search text
    https://www.healthgrades.com/search?search=knee+replacement+surgeon

    The exact URL you're on, plus whichever search or message field the allowlist kept for that site.

  • Page title
    knee replacement surgeon - Healthgrades

    The browser tab's title at the time of the visit, stored alongside the URL.

  • URL hash (database key)
    roomStats/-N3f7QaZ1kP9xQzR2b8H/info

    A hash of the URL used as the record's key. Anyone who visits the same URL, or is given the hash, lands on the same record.

  • Your VCU account ID
    uid: 7f3a9c21-88de-4b6a-9c11-2ee0a9f74210

    Your VCU user ID is attached to the presence write, tying the page visit to your account.

Where the record ends up

    • vcu-extension.firebaseio.com

    Firebase Realtime Database project operated by VCU for presence/page-visit records; its security rules, not the extension, govern read access beyond VCU's own client.

Observation

Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.

Where it sends data

Destinations our analysis observed VCU - The Co-Browsing Network 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.

  • vcu-extension.firebaseio.com

    VCU - The Co-Browsing Network sends data to vcu-extension.firebaseio.com. No other extension we have analyzed sends data here.

  • other VCU users co-browsing the same URL

    VCU - The Co-Browsing Network sends data to other VCU users co-browsing the same URL. Named as a recipient in this extension's own analysis.

Updated 2 November 2026bonnhbndeklljlliheakklnnbmoebdoh