Is Ninja ASO - Best ASO Toolbox For Google Play safe?

Medium risk

Ninja ASO is medium risk. On a Google Play app page, Ninja ASO reads the package ID from the URL and sends it within 150ms to appbrain.com and appstorespy.com, no consent prompt. Appstorespy's request uses a spoofed User-Agent; results cache 12h.

Youba Amjv1.4.0Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

App ID Sent to Third-Party ASO Services on Google Play Visits

On a Google Play app page, Ninja ASO reads the package ID from the URL and sends it within 150ms to appbrain.com and appstorespy.com, no consent prompt.

Appstorespy's request uses a spoofed User-Agent; results cache 12h.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You open any app's page on Google Play, such as https://play.google.com/store/apps/details?id=com.whatsapp.

The extension did this

The extension reads the app's package ID from the URL and automatically sends it to appbrain.com and appstorespy.com within 150 milliseconds, with no prompt or opt-in.

The background worker fetches the full appbrain.com page for that app and POSTs a GraphQL query to appstorespy.com/graphql concurrently. The appstorespy request includes a hardcoded spoofed desktop Windows User-Agent string.

Captured request
POSThttps://appstorespy.com/graphql

Returns ASO metadata: install count, feature flag, version, size, updated date, released date.

Headers
accept
*/*
user-agent
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36
content-type
application/json
accept-language
en-US,en;q=0.9
Body
{  "operationName": "getAppById",  "variables": {    "id": "com.whatsapp",    "store": "play"  },  "query": "query getAppById($store: ID!, $id: ID!) {\n  app(store: $store, id: $id) {\n    installs\n    feature\n    version\n    size\n    updated\n  released\n  }\n  }\n"}
Captured request
GEThttps://www.appbrain.com/app/com.whatsapp

Returns an HTML page; the extension scrapes it for downloads, ranking, APK size, Android version, library count, maturity rating, change logs, and keywords.

The code that does this

Content script: URL check and scrape trigger

Readable version

Readable equivalent

// Runs immediately when the content script loads on a Play Store app details page.if (document.location.href.startsWith('https://play.google.com/store/apps/details?id=')) {  setTimeout(async () => {    // Extract the app package ID from the URL query string.    const packageName = new URL(location.href).searchParams.get('id');    // Send it to the background service worker for third-party lookup.    await chrome.runtime.sendMessage({ type: 'scrape', packageName });  }, 150); // fires 150 ms after page load}
The code that does this

Background service worker: concurrent appbrain + appstorespy fetch

Readable version

appstorespy fetch function (ec)

// Sends the app package ID to appstorespy.com via a GraphQL query.// The User-Agent header is hardcoded and does not match the user's browser.async function fetchAppStoreSpy(packageName) {  const headers = new Headers();  headers.append('accept', '*/*');  headers.append('accept-language', 'en-US,en;q=0.9');  headers.append('content-type', 'application/json');  // Hardcoded spoofed Windows desktop User-Agent  headers.append('user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36');  const body = {    operationName: 'getAppById',    variables: { id: packageName, store: 'play' },    query: `query getAppById($store: ID!, $id: ID!) {  app(store: $store, id: $id) {    installs    feature    version    size    updated    released  }}`  };  const response = await fetch('https://appstorespy.com/graphql', {    method: 'POST',    headers,    body: JSON.stringify(body),    redirect: 'follow'  });  return response.json();}

Parallel dispatch and 12-hour cache write (hc)

// On cache miss: fetch both sources in parallel, then store for 12 hours.const [appbrainResult, appstoreSpyResult] = await Promise.all([  fetchAppBrain(packageName),    // zo(): GET appbrain.com/app/<packageName>  fetchAppStoreSpy(packageName)  // ec(): POST appstorespy.com/graphql]);const combined = { appbrain: appbrainResult, appstorespy: appstoreSpyResult };const expiry = Date.now() + 43_200_000; // 12 hours in ms (432e5)await chrome.storage.local.set({ [packageName]: { data: combined, timestamp: expiry } });
Third-party services that receive the app package ID
    • appstorespy.com

    ASO analytics platform. Receives the app package ID via a POST GraphQL query (getAppById) and returns install count, feature flag, version, size, release and update dates.

    • www.appbrain.com

    Android app index. Receives the package ID in a GET request URL path and returns downloads, ranking, APK size, library list, change logs, and keyword data via HTML scraping.

What's stored on your device

The extension caches ASO data for each app you viewed for up to 12 hours, keyed by package ID, a local history of apps you researched.

Location
chrome.storage.local, key = <packageName> (e.g. 'com.whatsapp')
Contents
{  "com.whatsapp": {    "data": {      "appbrain": {        "data": {          "appData": {            "size": "52 MB",            "ranking": "#1 in Communication",            "installs": "5,000,000,000+",            "maturity": "Everyone",            "androidVersion": "5.0+",            "librariesCount": "47"          }        },        "error": null      },      "appstorespy": {        "data": {          "size": "54525952",          "feature": true,          "updated": "2024-05-14",          "version": "2.24.10.82",          "installs": "5000000000",          "released": "2009-11-10"        },        "error": null      }    },    "timestamp": 1748900400000  }}

What it can do

Permissions this extension asks for, as declared in version 1.4.0. 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 www.appbrain.com

    https://www.appbrain.com/app/*

  • Read and change your data on appstorespy.com

    https://appstorespy.com/apps/play/*

  • Read and change your data on play-lh.googleusercontent.com

    https://play-lh.googleusercontent.com/*

  • Store data in your browser

    storage

Updated 30 September 2026dhdlnldaaeochhponebahbecamjdnkai