Is Trustpilot: Reviews & Ratings safe?

Medium risk

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.…

Trustpilotv2.675.0Chrome Web Store
52Risk
Who publishes it

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.

Publisher
Trustpilot
Declared legal entity
Trustpilot
Registered address
Pilestræde 58, 5th floor, København 1112, DK
Registered contact
Sam Coulson

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.

cdn.trustpilot.net
Also called by 2 other listings, including Check Trustpilot

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
What actually happens
You did this

You interact with the Trustpilot widget, for example, clicking 'Hide on this site' or viewing the rating overlay.

The extension did this

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.

What's stored on your device

A random UUID assigned on install. Persists in synced storage, rides every analytics event, letting Segment correlate interactions.

Location
chrome.storage.sync key 'anonymousId'
Contents
c49fbf92-28a9-4dc7-ba3a-dc74a94352d6
Fields sent with every Segment analytics event
  • Anonymous ID
    c49fbf92-28a9-4dc7-ba3a-dc74a94352d6

    A persistent UUID tied to your browser install. Links all your widget interactions over time.

  • User agent string
    Mozilla/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 size
    1512 × 982

    The pixel dimensions of your browser window.

  • Timezone
    Europe/London

    Your local timezone, as reported by the browser.

  • Locale / language
    en-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 version
    2.675.0

    Which version of the Trustpilot extension is installed.

Captured request
POSThttps://api.segment.io/v1/batch

HTTP 200 OK from Segment's batch ingestion endpoint.

Headers
Content-Type
application/json
Authorization
Basic a080NGVveUpybGhueU42Q0oyaW1iWTV6MHVLMUNWQzU6
Body
{  "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"      }    }  ]}
The code that does this

SegmentTracker class, persistent ID generation and event dispatch

Readable version

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;    });  }}
Where analytics data is sent
    • 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
What actually happens
You did this

You open or navigate to any website.

The extension did this

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.

Captured request
GEThttps://browserextension.trustpilot.com/v1/business-units/find?name=www.amazon.com

JSON object with businessUnit details (id, displayName, score.trustScore, numberOfReviews, isClaimed). Returns 404-style error JSON when the domain has no Trustpilot listing.

Headers
apikey
d5ccd2733fe8820c37fb4e501e0c1694
X-Trustpilot-Version
2.675.0
X-Trustpilot-Platform
BrowserExtension
The code that does this

Content script hook that fires on every page load

Readable version

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;}
Where visited hostnames are sent
    • 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
What actually happens
You did this

You perform a Google search on any of 190+ Google country domains.

The extension did this

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.

Captured request
GEThttps://browserextension.trustpilot.com/v1/business-units/find?name=www.amazon.co.uk

JSON with businessUnit data for the queried domain. One such request fired per result domain in the SERP.

Headers
apikey
d5ccd2733fe8820c37fb4e501e0c1694
X-Trustpilot-Version
2.675.0
X-Trustpilot-Platform
BrowserExtension
The code that does this

Search result annotation function that triggers the mass domain lookups

Readable version

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;}
Why this reveals search queries

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.

Where the SERP domain data is sent
    • 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

Updated 30 September 2026dimkjjladgaekdlfebknbaooicmfalke