Is WOT: Website Security & Safety Checker safe?
This extension shows website safety ratings and warns about suspicious or harmful sites.
It provides real-time safety ratings while you browse, shop, and search, and can warn about suspicious or harmful links in emails. It also offers password data-breach monitoring, adult content blocking, safe shopping alerts, and community site reviews. The store description says WOT collects browsing data to enable real-time protection and website reputation features.
Who publishes itWOT Services Oy - no other listings under this identity, 10 shared hostnames
WOT Services Oy - no other listings under this identity, 10 shared hostnames
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 10 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
WOT sends analytics identifiers during extension events
WOT installs, updates, or remote-config changes trigger analytics records with a UUID, version, locale, and premium state, POSTed to WOT's event and UUID-sync endpoints.
A separate `/reportClientData` endpoint exists but wasn't observed.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You install, update, or activate WOT in the browser.
A remote configuration update can also trigger the user-data analytics path.
The extension sends analytics records tied to a persistent UUID to WOT-operated endpoints.
The code also prepares a remote-configuration-driven report that can include selected extension state fields.
Observed during dynamic analysis: the POST body was 1,949 bytes and carried an encoded analytics event value.
Observed during dynamic analysis: the extension posted the persistent installation UUID to WOT's web app sync endpoint.
- Content-Type
- application/json
{ "uuid": "b2e47d55-1e74-4f40-a49d-01c524552770"}- Persistent installation UUIDb2e47d55-1e74-4f40-a49d-01c524552770
Lets WOT link analytics activity from the same browser profile across sessions.
- Event typeEXTENSION_INSTALL
Shows which extension event or configured report caused the analytics record.
- Extension version5.9.6
Connects the analytics record to the exact WOT build running in your browser.
- Locale and client contextChrome 124 on Windows, en-US
Adds language, browser, operating-system, and user-agent context to the event.
- Premium and opt-in stateTier: Free; is_premium: false; is_opt_in: true
Shows whether your extension profile is treated as paid, trialing, or opted in for protection features.
- Configured state fieldsuser, settings, version
A server-provided configuration can choose state fields from the extension store and include their current values in a report.
Extension code that builds and sends the analytics records
Install/update events create an analytics payload
deobfuscated/scripts/background/index.jswebextApi.runtime.onInstalled.addListener(e => { const t = "install" === e.reason, s = "update" === e.reason; initApp(s, t); const a = webextApi.runtime.getManifest().version, n = { os: -1 !== navigator.userAgent.indexOf("Mac") ? "mac" : -1 !== navigator.userAgent.indexOf("win") ? "windows" : "", eventSpecificFields: { version_number: a, full_user_agent: navigator.userAgent } }; t ? WotApi.sendAnalyticsFromBackground(ANALYTICS.ANALYTICS_EVENT_TYPES.EXTENSION_INSTALL, "", "", n) : s && WotApi.sendAnalyticsFromBackground(ANALYTICS.ANALYTICS_EVENT_TYPES.EXTENSION_UPGRADE, "", "", n)}), setTimeout(() => { initApp(!1, !1)}, 500);WotApi posts events and user-data reports
deobfuscated/scripts/shared/wotApi.jssendAnalyticsFromBackground(t, e, s, r) { const i = { [GA_ACTION_KEY]: t || "", [GA_LABEL_KEY]: e || "", testGroup: s, eventPayload: r }; this.sendInternalAnalytics(i)}async sendInternalAnalytics(t) { try { const e = t[GA_ACTION_KEY], s = t[GA_LABEL_KEY], r = t.testGroup, i = t.eventPayload, n = await this.getOrInitAnalyticsInfoFromLocalStorage(), o = new WotAnalyticsEvent(e, s, r, i, n); await o.loadStateFields(); const a = this.methods.analytics.url; this.postJson(a, o.createPayload()).catch(t => {}) } catch (t) {}}sendUserDataAnalytics(t, e, s, r) { try { const i = new UserDataAnalyticsEvent(t, e, s, r); this.postJson(this.methods.userDataAnalytics.url, i.createPayload(), headers).catch(t => {}) } catch (t) {}}Analytics event classes encode the event body
deobfuscated/scripts/shared/utils.jsclass AnalyticsEvent { createPayload() { return { ev: btoa(Array.from(JSON.stringify(this)).map((e, t) => e.charCodeAt(0) - (this.uuid.length + 2 * t)).toString()), se: this.uuid } }}class WotAnalyticsEvent extends AnalyticsEvent { constructor(e, t, n, o, i) { super(), this.product = ANALYTICS.ANALYTICS_PRODUCT, this.platform = ANALYTICS.ANALYTICS_PLATFORM, this.uuid = i, this.timestamp = Date.now(), this.resource = ANALYTICS.ANALYTICS_RESOURCE_TYPES.ORGANIC, this.country = getLocale(), this.user_agent = browserName, this.event_type = e, this.purchase_source = t, this.test_group = n, this.payload = o || {}, this.payload.extension_version = VERSION } async loadStateFields() { const e = await getExtensionState(); let t = !1, n = !1; e && (e.user && e.user.isPremiumUser && (t = e.user.isPremiumUser), e.settings && e.settings.protection && (n = e.settings.protection)), this.payload.is_premium = t, this.payload.is_opt_in = n }}class UserDataAnalyticsEvent extends AnalyticsEvent { constructor(e, t, n, o) { super(), this.product = "WOT", this.platform = "extension", this.uuid = WOT_TRACE_ID.value.slice(3), this.timestamp = Date.now(), this.country = getLocale(), this.eventMessage = e, this.versionNumber = t, this.isPremiumUser = n, this.userState = o }}Remote configuration selects state fields for /reportClientData
deobfuscated/scripts/background/app/aliases.js[HANDLE_USER_DATA_ANALYTICS]: e => (t, a) => { try { const t = e.data.forUpdate[USER_DATA_ANALYTICS]; if (t) { const e = a(), s = e.remoteConfig.features[USER_DATA_ANALYTICS]; if (Date.now() > t.endDate || s && s.version === t.version) return; const r = t.eventName, i = t.stateFields, n = VERSION, o = e.user, d = !!o && o.isPremiumUser, A = {}; i.forEach(t => { A[t] = e[t] }), WotApi.sendUserDataAnalytics(r, n, d, A) } } catch (e) {}}UUID sync posts a JSON body to the web app endpoint
deobfuscated/scripts/shared/wotApi.jsasync syncAnalyticInfoWithWebapp() { let t, e; try { const s = await fetch(ANALYTIC_INFO); 200 === s.status && (t = await s.json()), e = t && t.uuid || generateUuidV4(), await fetch(ANALYTIC_INFO, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ uuid: e }) }) } catch (t) {} return e}Additional Mixpanel tracking path in the extension source
var _extends = Object.assign || function(e) { for (var r = 1; r < arguments.length; r++) { var t = arguments[r]; for (var s in t) Object.prototype.hasOwnProperty.call(t, s) && (e[s] = t[s]) } return e};class Mixpanel { constructor(e) { this.store = e, this.mixpanel = mixpanel.init("9c2a5bc5df124e35970da2299c103485") } getSuperProperties(e) { const r = e.user || {}, t = new UAParser(navigator.userAgent), s = t.getOS().name; return { $browser: t.getBrowser().name, $browser_version: t.getBrowser().version.split(".")[0], $os: `${s}${"Mac OS"===s?" X":""}`, Platform: "Web", platform_source: "extension", "Is Logged In": !!r.mail, Tier: r.isPremiumUser ? "Premium" : "Free", $app_version_string: e.version.versionNumber, "Had Trial": !(!r.isPremiumUser && !r.isTrialing) } } flushQueue(e, r, t) { e && (t.forEach(t => { this.track(t.eventType, t.properties, e, r) }), this.store.dispatch(deleteMixpanelQueue())) } send(e) { const r = e.eventType; var t = e.properties; const s = void 0 === t ? {} : t, i = this.store.getState(); if (!i.isAutomation) { const e = _extends({}, this.getSuperProperties(i), s), t = i.mixpanel.distinctId; if (t) { const s = i.userIp; this.track(r, e, t, s) } else this.store.dispatch(addMixpanelEventToQueue({ eventType: r, properties: e })) } } track(e, r, t, s) { this.mixpanel.track(e, _extends({}, r, { distinct_id: t, ip: s })) }}Decodes a captured WOT analytics `/event` JSON body so you can inspect the encoded event fields.
- Node.js 18+
#!/usr/bin/env nodeconst fs = require('fs');const input = fs.readFileSync(0, 'utf8').trim();if (!input) { console.error('Usage: node wot-analytics-decode.js < captured-event-body.json'); process.exit(1);}const payload = JSON.parse(input);if (typeof payload.ev !== 'string' || typeof payload.se !== 'string') { throw new Error('Expected JSON with string fields ev and se');}const csv = Buffer.from(payload.ev, 'base64').toString('utf8');const numbers = csv.split(',').map((value) => { const parsed = Number(value); if (!Number.isFinite(parsed)) { throw new Error(`Invalid encoded value: ${value}`); } return parsed;});const decoded = numbers .map((value, index) => String.fromCharCode(value + payload.se.length + 2 * index)) .join('');console.log(JSON.stringify(JSON.parse(decoded), null, 2));- 1node wot-analytics-decode.js < captured-event-body.json
- analytics.api.mywot.com
WOT analytics host; dynamic analysis observed a POST to `/event`, and source code also defines `/reportClientData`.
- app.mywot.com
WOT web app host; dynamic analysis observed the extension POSTing its persistent UUID to `/api/analytics/sync`.
- api.mixpanel.com
Mixpanel analytics host compiled into the extension bundle; the WOT wrapper adds browser, OS, tier, distinct ID, and IP fields when it sends Mixpanel events.
What it can do
Permissions this extension asks for, as declared in version 5.9.6. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 5.9.2, which we have not unpacked yet.
Read and change your data on www.mywot.com
http://www.mywot.com/*
Read and change your data on api.mywot.com
http://api.mywot.com/* and 1 more
Read and change your data on every site you visit
http://*/*
Read and change your data on every secure site you visit
https://*/*
See the address and title of every tab you have open
tabs
Add items to the right-click menu
contextMenus
See every page you navigate to, as you navigate to it
webNavigation
Watch every request your browser makes
webRequest
Block and redirect the requests your browser makes
declarativeNetRequest
See which of your requests its blocking rules matched
declarativeNetRequestFeedback
Store data in your browser
storage
Run its own code inside the pages you visit
scripting
Schedule its own background tasks
alarms
Where it sends data
Destinations our analysis observed Web of Trust 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.
- WOT
Web of Trust sends data to WOT. Named as a recipient in this extension's own analysis.