Is HubSpot Templates for Gmail™ by cloudHQ safe?
HubSpot Templates for Gmail reads every signed-in Google account email and sends the list to cloudhq.net on each Gmail load.
When Gmail loads, the extension fetches mail.google.com account-switcher pages for up to 20 signed-in accounts and extracts each account's email address. It then posts that list to cloudhq.net, at most once every two weeks, without waiting for sign-in or any user action.
Who publishes itcloudHQ LLC - 19 other listings from the same operator, 7 of them carrying a finding
cloudHQ LLC - 19 other listings from the same operator, 7 of them carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
19 other listings published from this account, 189k+ users between them. 7 of them carry a finding.
Shared hosts - 7 hostnames
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
cloudHQ Gmail extension uploads every signed-in Google account email on load
Code analysis shows the extension probes up to 20 signed-in Google accounts on Gmail load and sends all their emails to cloudhq.net, before any sign-in or consent step.
Repeats roughly every two weeks.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Static analysis
You open Gmail while signed in to one or more Google accounts.
The extension looks up your other signed-in Google accounts and sends their email addresses to cloudhq.net.
This runs at startup, ahead of any login or signup dialog.
Account probe loop
var i = "https://mail.google.com/mail/u/" + r.g_switcher_account_emails_index + "/?sw=2";fetch(i, { method: "GET", mode: "cors" }).then((e => e.text())).then((t => { var n = /var GLOBALS=([\s\S]*?)\;/gm, i = null; for (; i = n.exec(t);) { var o = JSON.parse(JSON.stringify(i[1])).split(","); for (var a = 0; a < o.length; a++) if (o[a] && r.fn_validate_email(o[a].replaceAll('"', ""))) { var s = o[a].replaceAll('"', "").toLowerCase(); r.g_switcher_account_emails.push(s); return void r.fn_get_switcher_account_emails(e) } }}))Throttled upload to cloudhq.net
chrome.storage.local.get(n, (function(r) { if (!r || !r[n] || !r[n].timestamp || r[n].timestamp < t.g_two_weeks_ago_timestamp) try { t.fn_get_switcher_account_emails((function() { t.fn_ajax({ type: "POST", url: t.g_server_url + "main_pre_user/check", dataType: "json", data: { email: t.g_email_or_login, email_switcher_list: t.g_switcher_account_emails, email_or_login: t.g_email_or_login, chrome_extension_name: t.g_cloudHQ_feature_name, switch_login: "1" }, complete: function(e, t) { if (-1 === ["error", "timeout", "abort"].indexOf(t)) try { chrome.storage.local.set(i, (function() {})) } catch (e) {} } }) }), e) } catch (e) { console.log("e:", e) }}))- Active account emailalex.morgan@gmail.com
The Gmail address the extension is running under.
- Other signed-in accounts["alex.morgan@gmail.com","amorgan@examplecorp.com"]
Every Google account email found in the browser, including personal and work.
- Feature namegmail_templates
Which cloudHQ feature is installed.
- Switch login flag1
Constant flag sent with the request.
- www.cloudhq.net
Receives the account list at /main_pre_user/check; the extension's own vendor domain.
Searches the extension's content script for the account probe, upload endpoint and throttle logic.
- Node.js 18+
// Static check: confirm the account-enumeration and upload code is present in the shipped content script.// Usage: node check.js path/to/content.jsconst src = require("fs").readFileSync(process.argv[2], "utf8");const checks = { "probes /mail/u/N/?sw=2": /mail\/u\/"\s*\+\s*\w+\.g_switcher_account_emails_index\s*\+\s*"\/\?sw=2/, "posts to main_pre_user/check": /main_pre_user\/check/, "sends email_switcher_list": /email_switcher_list\s*:/, "two-week throttle": /g_two_weeks_ago_timestamp\s*=\s*new Date\(Date\.now\(\)\s*-\s*12096e5\)/, "runs at startup": /\.fn_pre_user_check\(\)/,};for (const [name, re] of Object.entries(checks)) console.log((re.test(src) ? "FOUND " : "MISSING ") + name);- 1Unpack the CRX.
- 2Run node check_cloudhq_switcher.js content.js.
- 3FOUND lines confirm the code is present.
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.
What it can do
Permissions this extension asks for, as declared in version 1.0.2.10. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on mail.google.com
https://mail.google.com/
Read and change your data on www.cloudhq.net
https://www.cloudhq.net/
Read and change your data on hubspot.com
https://*.hubspot.com/*
Read and change your data on hubspotpreview-eu1.com
https://*.hubspotpreview-eu1.com/*
Read and change your data on hubspotpreview-na1.com
https://*.hubspotpreview-na1.com/*
Run its own code inside the pages you visit
scripting
Store data in your browser
storage
Keep running in the background while your browser is open
background
See the address and title of every tab you have open
tabs
Where it sends data
Destinations our analysis observed HubSpot 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.
- www.cloudhq.netwidely used
HubSpot sends data to www.cloudhq.net. A widely used service: 79 other extensions we have analysed send data here.