Is APK Downloader safe?

Medium risk

Search and download free Android app APK or XAPK files from APKPure.

This extension provides access to APKPure’s online APK downloader for finding free apps and games by app name or package name. It redirects users to an app information page where they can download available APK or XAPK files, including free apps that may be region-restricted on Google Play.

APKPurev1.0.8Chrome Web Store
54Risk
Who publishes it

ELECYBER - no other listings under this identity

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
APKPure
Declared legal entity
ELECYBER
Registered contact
ELECYBER INTERNATIONAL PTE. LTD.

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Content script scans every page's DOM and reports it internally on load

MediumunwantedCWE-200Dynamic sandbox

APK Downloader's content script runs on every site, scanning the DOM for schema.org data on load and reporting the domain plus product fields to the background worker.

What actually happens

You did this

You open, switch to, or navigate within any webpage in the browser.

The extension did this

The extension's content script immediately scans the page's DOM for structured product data and reports the domain and any extracted fields to the background service worker.

This happens at document_start, before the page has finished loading, on every site, not only app-store listings.

What the content script extracts and sends to the background script on every page

  • Page domain
    www.example-shop.com

    The hostname of every site you visit is included in the internal message, whether or not the site is an app store.

  • Structured product/app fields
    com.whatsapp

    Any name, price, developer, rating, or package-ID data the page exposes via schema.org markup is pulled, on any site carrying it.

  • Page title
    WhatsApp Messenger

    The extracted title of the product/app the page describes.

The code that does this

Content script scrapes DOM on load and on every SPA navigation (extract-package-info.605807e3.js)

Readable version
const config = { run_at: "document_start" };// Runs immediately when the content script loads on ANY page.async function reportPageInfo() {  chrome.runtime.sendMessage(buildPkgInfoMessage());}function buildPkgInfoMessage() {  return {    type: messageType.pkgInfo,    data: extractProductDataFromDOM()  };}function extractProductDataFromDOM() {  // Look for schema.org itemprop="offers" elements  const offersEls = document.querySelectorAll('[itemprop="offers"]');  let extracted = {};  const itempropEls = document.querySelectorAll('[itemprop]');  const jsonLdEls = document.querySelectorAll('[type="application/ld+json"]');  const wantedFields = ['image', 'name', 'priceCurrency', 'price', 'ratingValue', 'ratingCount', 'publisher', 'worstRating', 'bestRating'];  const productTypes = ['MobileApplication', 'SoftwareApplication', 'Product', /* schema.org variants */];  // Try JSON-LD structured data first (runs on EVERY page with a matching <script type="application/ld+json">)  try {    jsonLdEls.forEach(el => {      const data = JSON.parse(el.innerText);      // ...matches against productTypes and captures the object into `extracted`    });  } catch (e) {}  // Fall back to itemprop microdata attributes if no JSON-LD matched  if (itempropEls.length && !Object.keys(extracted).length) {    itempropEls.forEach(el => {      wantedFields.forEach(field => {        if (el.getAttribute('itemprop') === field) {          extracted[field] = el.content || el.src || el.innerText;        }      });    });  }  // Per-site adjustments for ~23 known app-store domains (Google Play, App Store, APKPure, etc.)  // ...  return {    url: extracted.url,    domain: window.location.hostname,   // sent for EVERY page, whitelisted or not    packageId: extracted.packageId,    icon: extracted.image,    title: extracted.name,    priceCurrency: extracted.priceCurrency,    price: extracted.price,    developer: extracted.publisher,    ratingValue: extracted.ratingValue,    reviewCount: extracted.ratingCount,    worstRating: extracted.worstRating,    bestRating: extracted.bestRating  };}// Fires once on script load, and again on every SPA navigation.reportPageInfo();chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {  if (msg.type === messageType.getPkg) sendResponse(buildPkgInfoMessage());});window.addEventListener('hashchange', reportPageInfo);window.addEventListener('pushState', reportPageInfo);window.addEventListener('popstate', reportPageInfo);

The code that does this

Background worker keeps data only for a hardcoded domain whitelist (background.93e42914.js)

Readable version
// Called for every pkgInfo message received from the content script, on every page.function handlePkgInfo(message, tabId) {  const isWhitelistedDomain = schemaWhiteUrlList.some(host => message.data.domain === host);  // Record whether this page's domain was on the app-store whitelist — for EVERY page.  chrome.storage.local.set({ isWhiteUrl: isWhitelistedDomain });  if (!isWhitelistedDomain) {    clearBadge(tabId);    return; // extracted data is discarded here, not persisted  }  setBadge(tabId, message.data.title ? '1' : '');  chrome.storage.local.set({    data: message.data.title ? JSON.stringify(message.data) : null,    tabId: tabId  });}chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {  if (message?.type === messageType.pkgInfo) {    handlePkgInfo(message, sender?.tab?.id);  }});

What's stored on your device

Every load writes isWhiteUrl true/false. Whitelisted domains also save scraped fields; 76 of 89 tested loads discarded data.

Location
chrome.storage.local keys 'isWhiteUrl' and 'data'
Contents
{  "isWhiteUrl": false,  "note_non_whitelisted": "Observed 76 times during dynamic analysis testing on ordinary, non-app-store pages — each one a page load that triggered the DOM scan and internal message, then had its extracted data discarded because the domain wasn't on the whitelist.",  "data_when_whitelisted_example": {    "price": "0",    "title": "WhatsApp Messenger",    "developer": "WhatsApp LLC",    "packageId": "com.whatsapp",    "ratingValue": "4.2"  }}

Tab URL Sent to pureapk.com on Every Popup Open, Search, and Download

MediumunwantedCWE-200Dynamic sandbox

Opening the popup, searching, or clicking download sends your tab URL and a persistent device ID to tapi.pureapk.com.

What actually happens

You did this

You open the extension popup on any browser tab, or type a search, or click the Download button.

The extension did this

The extension reads the URL of your current tab and sends it to tapi.pureapk.com together with a persistent device ID unique to your installation.

The beacon fires for three distinct actions: popup open (event_code=open), search bar submission (event_code=search), and download button click (event_code=download).

Captured request

GEThttps://tapi.pureapk.com/report/search_report?atta_id=05200075581&random=0.8631820457615105&client_id=QwWhmueBPOqEA16zFKKBe&event_code=open&url=https%3A%2F%2Fwww.example.com%2Fa planted marker value

HTTP 403. Endpoint confirmed reachable; the client_id (QwWhmueBPOqEA16zFKKBe) matched the value in chrome.storage.local key 'apk-downloader-uid'. A planted marker value appeared verbatim in the query string.

What the extension sends on each popup open, search, or download

  • Current tab URL
    https://www.example.com/a planted marker value

    The full address of the page you have open when you interact with the extension.

  • Device ID (client_id)
    QwWhmueBPOqEA16zFKKBe

    A permanent random ID created on install, stored locally, and sent every request, letting the server track your tab URLs over time.

  • App partner ID (atta_id)
    05200075581

    A hardcoded value that identifies this extension to the pureapk.com backend. The same for all users.

  • Action type (event_code)
    open

    Which user action triggered the beacon: opening the popup, running a search, or clicking download.

  • Noise salt (random)
    0.8631820457615105

    A random float added to each request, likely to prevent caching, not for privacy.

What's stored on your device

Created via nanoid on install, never rotated. Sent as client_id on every request, tying every tab URL you report to the same device.

Location
chrome.storage.local key 'apk-downloader-uid'
Contents
{  "apk-downloader-uid": "QwWhmueBPOqEA16zFKKBe"}

The code that does this

The reportAtta function that fires the beacon, shipped minified vs. readable.

Readable version

reportAtta — creates or retrieves persistent UUID, then sends beacon

// Called by background onMessage handler whenever popup sends a 'report' message.async function reportAtta(eventCode, extraInfo = {}) {  // Read the persistent device UUID from storage (creates one on first run).  let { 'apk-downloader-uid': clientId } = await chrome.storage.local.get('apk-downloader-uid');  if (!clientId) {    clientId = nanoid; // generates a URL-safe random 21-char ID    await chrome.storage.local.set({ 'apk-downloader-uid': clientId });  }  // Build the query string and fire the GET beacon.  const params = new URLSearchParams({      atta_id: '05200075581', // hardcoded partner ID      random: Math.random.toString, // cache-busting noise      client_id: clientId, // persistent per-device UUID      event_code: eventCode, // 'open' | 'search' | 'download' | 'install'      ...extraInfo // includes { url: <current_tab_url> }  });  await fetch(`https://tapi.pureapk.com/report/search_report?${params}`);}

Popup mount effect — triggers open beacon with current tab URL

// Runs once when the popup loads.useEffect(async => {    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });    if (!tab) return;    // Send 'open' event to background with the current tab's URL.    chrome.runtime.sendMessage({        type: 'report',        data: {          eventCode: 'open',          extraInfo: { url: tab.url || '' }        }    });    // ... then fetch APK info for the page  }, []);

Where the data is sent

    • tapi.pureapk.com

    Analytics API for APK Pure (pureapk.com), the extension's parent site. Receives the tab URL, persistent device ID, and event type on every popup interaction.

Reproduce it yourself

Run this in the DevTools console on the extension's background service worker to intercept all outbound beacons to tapi.pureapk.com and log their full query parameters in readable form.

Requires
  • Chrome with Developer mode enabled
  • APK Downloader extension installed
apk-downloader-beacon-watcher.js · js
// apk-downloader-beacon-watcher.js// Patches globalThis.fetch in the APK Downloader background service worker// to intercept and log every call to tapi.pureapk.com/report/search_report.//// Usage:// 1. Open chrome://extensions, enable Developer mode.// 2. Find "APK Downloader" and click its service worker link.// 3. In the DevTools console that opens, paste this entire script.// 4. Click the APK Downloader toolbar icon on any page.// 5. Watch for [APK_BEACON] entries in the console.(function {    const TARGET = 'tapi.pureapk.com/report/search_report';    const origFetch = globalThis.fetch.bind(globalThis);    globalThis.fetch = function (input, init) {      const url = typeof input === 'string' ? input : input?.url ?? '';      if (url.includes(TARGET)) {        try {          const parsed = new URL(url);          const params = {};          parsed.searchParams.forEach((v, k) => { params[k] = v; });          console.log('[APK_BEACON] Outbound beacon intercepted:');          console.log(' URL: ', url);          console.log(' client_id: ', params.client_id, ' ← persistent device UUID');          console.log(' event_code: ', params.event_code, ' ← open | search | download | install');          console.log(' url: ', params.url, ' ← current tab URL at time of action');          console.log(' atta_id: ', params.atta_id);          console.log(' random: ', params.random);          console.log(' full params:', params);        } catch (e) {          console.error('[APK_BEACON] Parse error:', e);        }      }      return origFetch(input, init);    };    console.log('[APK_BEACON_WATCHER] Installed. Open the APK Downloader popup to capture beacons.');});
How to run it
  1. 1Open chrome://extensions, enable Developer mode.
  2. 2Open APK Downloader's service worker inspector.
  3. 3Paste this script, press Enter.
  4. 4Visit any page, click the toolbar icon.
  5. 5Look for [APK_BEACON] logs with tab URL and client_id.

Persistent device ID and active tab URL sent to pureapk.com on popup open

MediumunwantedCWE-359Dynamic sandbox

Each time you open the APK Downloader popup on an HTTP page, the extension sends a permanent device ID and your tab's URL to tapi.pureapk.com.

What actually happens

You did this

You open the APK Downloader popup while on any HTTP page.

The extension did this

The extension immediately sends your permanent device ID and the current page URL to tapi.pureapk.com.

No download action required, opening the popup is sufficient to trigger the report.

Captured request

GEThttps://tapi.pureapk.com/report/search_report?atta_id=05200075581&random=0.7341829471&client_id=jRuJDKtkbHgh8TWIIg0Qz&event_code=open&url=https%3A%2F%2Fwww.example.com%2Fsome-page

Confirmed via dynamic analysis (DA network row 267): GET fired immediately on popup open with persistent client_id and active tab URL.

Data sent to tapi.pureapk.com on every popup open

  • Your permanent device ID
    jRuJDKtkbHgh8TWIIg0Qz

    A random identifier created on first install and stored forever. Links every popup open back to your specific browser installation.

  • The page you had open
    https://www.example.com/some-page

    The full URL of the active tab when you opened the popup, reveals which sites you were visiting at that moment.

  • Analytics account ID
    05200075581

    A hardcoded code that identifies this extension's analytics account at pureapk.com.

  • Random noise value
    0.7341829471

    A per-request random number included alongside the stable identifiers.

The code that does this

Tracking function in background service worker (background.93e42914.js:205-219)

Readable version
async function reportAtta(eventCode, extraInfo = {}) {  // Retrieve or create the persistent device identifier  let { [KEY_UID]: clientId } = await chrome.storage.local.get(KEY_UID);  if (!clientId) {    clientId = nanoid();    await chrome.storage.local.set({ [KEY_UID]: clientId });  }  // Build query string with permanent ID + current event data  const params = new URLSearchParams({    atta_id: "05200075581",       // hardcoded analytics account    random: Math.random().toString(),    client_id: clientId,          // persistent device ID    event_code: eventCode,    ...extraInfo                  // includes { url: activeTabUrl }  });  await fetch(`https://tapi.pureapk.com/report/search_report?${params}`);}

Where the data is sent

    • tapi.pureapk.com

    Analytics backend operated by pureapk.com, the extension's developer. Receives the persistent device ID, active tab URL, and event type on every popup open.

What it can do

Permissions this extension asks for, as declared in version 1.0.8. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every secure site you visit

    https://*/*

  • See the address and title of every tab you have open

    tabs

  • Store data in your browser

    storage

  • Add items to the right-click menu

    contextMenus

  • Act on the current tab, but only after you click the extension

    activeTab

Where it sends data

Destinations our analysis observed APKPure 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.

  • APKPure.com

    APKPure sends data to APKPure.com. No other extension we have analyzed sends data here.

Updated 15 October 2026glngapejbnmnicniccdcemghaoaopdji