Is VCU - The Co-Browsing Network safe?

Medium 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
45Risk

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 FOUND

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

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.

01EvidenceCAUSE EFFECT
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.

02EvidenceCODE COMPARE
The code that does this

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

What it actually does
Beautified: the same domain->params tablebackground.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 domainbackground.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
    }
03EvidenceCODE COMPARE
The code that does this

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

What it actually does
Beautified: fires on every completed navigationbackground.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 iframebackground.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
      }))
    }
04EvidenceCODE COMPARE
The code that does this

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

What it actually does
Beautified: setRoomStats and registerOnlineAtLocationchats.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
        }
05EvidenceFIELD TABLE
What gets written to the Firebase record for the page you visited
FieldValueWhy it matters
Page URL + matched search text
https://www.healthgrades.com/search?search=knee+replacement+surgeonThe exact URL you're on, plus whichever search or message field the allowlist kept for that site.
Page title
knee replacement surgeon - HealthgradesThe browser tab's title at the time of the visit, stored alongside the URL.
URL hash (database key)
roomStats/-N3f7QaZ1kP9xQzR2b8H/infoA 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-2ee0a9f74210Your VCU user ID is attached to the presence write, tying the page visit to your account.
06EvidenceTHIRD PARTY LIST
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.

07EvidencePLAIN NOTE
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.

Data recipients

vcu-extension.firebaseio.comother VCU users co-browsing the same URL
Updated 20 September 2026bonnhbndeklljlliheakklnnbmoebdoh