Is Trustpilot: Reviews & Ratings safe?
Trustpilot: Reviews & Ratings is medium risk. A persistent anonymous UUID is generated via crypto.randomUUID(), stored in chrome.storage.sync. Widget interactions send events to Segment (api.segment.io) with your user agent, viewport, timezone, locale, hostname. 14 POSTs per session.…
Who publishes itTrustpilot - no other listings under this identity, 1 shared hostname
Trustpilot - no other listings under this identity, 1 shared hostname
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 1 hostname
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
Segment analytics tracks UI events with persistent anonymous ID
A persistent anonymous UUID is generated via crypto.randomUUID(), stored in chrome.storage.sync.
Widget interactions send events to Segment (api.segment.io) with your user agent, viewport, timezone, locale, hostname. 14 POSTs per session.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You interact with the Trustpilot widget, for example, clicking 'Hide on this site' or viewing the rating overlay.
The extension posts an analytics event to Segment, including your persistent anonymous ID, browser fingerprint data, and the hostname of the site you are on.
The anonymous ID is generated once and stored permanently in chrome.storage.sync, so it follows you across all browser sessions and synced devices.
A random UUID assigned on install. Persists in synced storage, rides every analytics event, letting Segment correlate interactions.
- Location
- chrome.storage.sync key 'anonymousId'
c49fbf92-28a9-4dc7-ba3a-dc74a94352d6- Anonymous IDc49fbf92-28a9-4dc7-ba3a-dc74a94352d6
A persistent UUID tied to your browser install. Links all your widget interactions over time.
- User agent stringMozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Your browser name, version, and operating system.
- User agent data{"brands":[{"brand":"Chromium","version":"128"}],"mobile":false,"platform":"Linux"}
Structured browser brand, version, and platform data from the User Agent Client Hints API.
- Viewport size1512 × 982
The pixel dimensions of your browser window.
- TimezoneEurope/London
Your local timezone, as reported by the browser.
- Locale / languageen-GB
Your browser's UI language and locale setting.
- Hostname (on 'Hide on this site')www.example.com
The domain of the site you are on when you click to hide the widget.
- Extension version2.675.0
Which version of the Trustpilot extension is installed.
HTTP 200 OK from Segment's batch ingestion endpoint.
- Content-Type
- application/json
- Authorization
- Basic a080NGVveUpybGhueU42Q0oyaW1iWTV6MHVLMUNWQzU6
{ "batch": [ { "type": "track", "event": "Button Clicked", "anonymousId": "c49fbf92-28a9-4dc7-ba3a-dc74a94352d6", "properties": { "name": "Hide on this site", "host": "www.bbc.co.uk" }, "context": { "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36", "userAgentData": { "brands": [ { "brand": "Chromium", "version": "128" } ], "mobile": false, "platform": "Linux" }, "viewportHeight": "982", "viewportWidth": "1512", "timezone": "UTC", "userLocale": "en-GB", "language": "en-GB", "version": "2.675.0" } } ]}SegmentTracker class, persistent ID generation and event dispatch
Readable: UUID creation and persistent storage
// service-worker.js:329 — SegmentTrackerclass SegmentTracker { constructor() { const writeKey = keys['SEGMENT_PRODUCTION_WRITE_KEY']; // 'kO4DOyJrlhnyN6CJ2imbY5z0uK1CVCik' if (!writeKey) return; this.analytics = new Analytics({ writeKey }); this.identify(); // loads or creates persistent anonymousId } track(event) { if (!this.analytics || !this.anonymousId) return; // Attach the persistent ID to every outbound event this.analytics.track({ ...event, anonymousId: this.anonymousId }); } identify() { chrome.storage.sync.get('anonymousId', (stored) => { if (!this.analytics) return; let id = stored.anonymousId; if (!id) { // First run: generate and persist a new UUID id = crypto.randomUUID(); chrome.storage.sync.set({ anonymousId: id }); this.analytics.identify({ anonymousId: id }); } this.anonymousId = id; }); }}- api.segment.io
Segment's event ingestion API. Receives widget interaction events with the persistent anonymous ID and device context. Segment (Twilio) is a US customer data platform.
Every visited hostname sent to Trustpilot API on page load
Each site you visit has its hostname sent via GET to Trustpilot's API at browserextension.trustpilot.com, disclosing the site. 13 requests seen across four sites in one session.
Responses cache 30 min, so only first visits are disclosed.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You open or navigate to any website.
The extension sends the site's hostname to Trustpilot's API server.
Fires automatically on every navigation to an uncached domain, no interaction with the extension widget required.
JSON object with businessUnit details (id, displayName, score.trustScore, numberOfReviews, isClaimed). Returns 404-style error JSON when the domain has no Trustpilot listing.
- apikey
- d5ccd2733fe8820c37fb4e501e0c1694
- X-Trustpilot-Version
- 2.675.0
- X-Trustpilot-Platform
- BrowserExtension
Content script hook that fires on every page load
Readable: fetch fires on every uncached hostname
// index.js:10510 — getBusinessUnitByNameasync function getBusinessUnitByName({ name, cacheKey }) { const encodedName = encodeURIComponent(name); // Check session cache first (TTL 1800 s) const cached = await cache.get(cacheKey); if (cached) return cached.businessUnit; // Cache miss: disclose hostname to Trustpilot API const response = await fetch( `https://browserextension.trustpilot.com/v1/business-units/find?name=${encodedName}`, { method: 'GET', headers: { apikey: 'd5ccd2733fe8820c37fb4e501e0c1694', // hardcoded 'X-Trustpilot-Platform': 'BrowserExtension', 'X-Trustpilot-Version': manifest.version } } ); const data = await response.json(); await cache.set(cacheKey, { businessUnit: data }); return data;}- browserextension.trustpilot.com
Trustpilot's browser-extension API endpoint. Receives the hostname of every site you navigate to when no cached response exists. Operated by Trustpilot A/S (Copenhagen, Denmark).
Google search result domains disclosed to Trustpilot API
On a Google search, the extension extracts the hostname of every result and looks each up at browserextension.trustpilot.com.
Queried domains mirror the search, indirectly revealing it. 9 simultaneous GETs seen for 'online shopping'.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You perform a Google search on any of 190+ Google country domains.
The extension extracts every result hostname from the page and sends a separate lookup request to Trustpilot for each one.
The complete set of queried domains mirrors the search results, allowing Trustpilot to infer what you searched for.
JSON with businessUnit data for the queried domain. One such request fired per result domain in the SERP.
- apikey
- d5ccd2733fe8820c37fb4e501e0c1694
- X-Trustpilot-Version
- 2.675.0
- X-Trustpilot-Platform
- BrowserExtension
Search result annotation function that triggers the mass domain lookups
Readable: one API lookup per result hostname
// index.js:15139 — annotateSearchResultsfunction annotateSearchResults() { // Scrape organic and sponsored result elements from the Google SERP const organicResults = getSearchResults(); const organicDomains = getHostnames(organicResults); // e.g. ['www.amazon.co.uk', 'www.asos.com', ...] const organicHandles = getSearchResultsRenderHandles(); const sponsoredResults = getSponsoredResults(); const sponsoredDomains = getHostnames(sponsoredResults); const sponsoredHandles = getSponsoredResultsRenderHandles(); // For each domain, inject a React component that calls useBusinessUnit // which in turn issues a GET to browserextension.trustpilot.com renderAnnotations(organicDomains, organicHandles, false); renderAnnotations(sponsoredDomains, sponsoredHandles, true /* isSponsored */);}// index.js:15105-15115 — each annotation componentfunction ResultAnnotationComponent({ domain, isSponsored }) { const { businessUnit } = useBusinessUnit({ domainString: domain, // the SERP result hostname isUrlDenied: false, isGoogleSearch: true // suppresses icon update; disclosure still occurs }); return businessUnit ? <RatingBadge score={businessUnit.score} ... /> : null;}Trustpilot receives the exact domain set returned by Google for a given query. Because search result domains are highly specific to the search term (searching for 'online shopping' returns a distinct cluster of retail domains versus searching for 'antivirus software'), the sequence of lookups constitutes a fingerprint of what you searched for — even though the search query string itself is never transmitted.
- browserextension.trustpilot.com
Trustpilot's browser-extension API endpoint. Receives one hostname lookup per Google search result domain. Operated by Trustpilot A/S (Copenhagen, Denmark).
What it can do
Permissions this extension asks for, as declared in version 2.675.0. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Store data in your browser
storage
Act on the current tab, but only after you click the extension
activeTab