Is Dropship - Product Research safe?
Dropship.io is high risk. Dropship - Product Research requests your Google account ID on install, stores it locally, and sends it to the Dropship API. A GET to users/rating was captured with the user token header set; the token is also on the app client.…
Who publishes itDropship FZCO - no other listings under this identity, 1 shared hostname
Dropship FZCO - 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
Google account ID reused as Dropship API token
Dropship - Product Research requests your Google account ID on install, stores it locally, and sends it to the Dropship API.
A GET to users/rating was captured with the user token header set; the token is also on the app client.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You install the Dropship extension.
The extension requests your Google account identifier, saves it as a local token, and attaches it to Dropship API requests.
A request to this endpoint was observed within the first 10 seconds after extension load.
- Extension-Token
- sdfkljsbdfgoipoasji2318743460r9edlfgjhpre896544534854368tfd34lfgkjbpfgepofdgsoidfsopdfjspodfjsidfjgphjmweorwjeoir5487623423jkdflgdkf874538953459834
- Extension-User-Token
- 107890220540720092498
- Your Google account identifier107890220540720092498
This is a stable account-derived value, so requests carrying it can be tied back to the same Google profile over time.
- Extension API tokensdfkljsbdfgoipoasji2318743460r9edlfgjhpre896544534854368tfd34lfgkjbpfgepofdgsoidfsopdfjspodfjsidfjgphjmweorwjeoir5487623423jkdflgdkf874538953459834
This fixed extension token identifies the client making the request and is sent alongside your account-derived token.
- Dropship API hostapi-ws-extension.prod.dropship.io
This is the remote service that receives the request containing the account-derived token.
The extension keeps the account-derived identifier locally and can reuse it after the first install-time lookup.
- Location
- chrome.storage.local key 'token'
{ "token": "107890220540720092498"}Where the account identifier becomes a request header
Install-time token creation and rating request
app/background.jschrome.storage.local.get(null, async function(e) { if (e.token) s = e.token; else { if (navigator.userAgent.includes("OPR")) s = R(); else { let e = chrome.identity.getProfileUserInfo({ accountStatus: "ANY" }); await (e?.then(e => { s = e?.id ? e?.id : R() })) } chrome.storage.local.set({ token: s }), chrome.storage.local.set({ adBlocker: !1 }), fetch(`${r}users/rating/`, { headers: { "Content-Type": "application/json", "Extension-Token": a, "Extension-User-Token": s }, method: "GET" }).then(e => e.json()).then(e => { chrome.storage.local.set({ rating: e?.rating || 0 }), chrome.runtime.sendMessage({ message: "getRateInner", rating: e?.rating || 0 }) }).catch(() => chrome.storage.local.set({ rating: 0 })).finally(() => chrome.storage.local.set({ rating: 0 })) }});App API client reusing the stored token
static/js/main.jsfunction e() { return en(this, e), this.baseURL = "https://api.dropship.io/api", e.instance || (this.create(), this.createInterceptor(), e.instance = this), e.instance}return nn(e, [{ key: "create", value: function() { var e = f(u.a.mark((function e() { var t, n, r = this; return u.a.wrap((function(e) { for (;;) switch (e.prev = e.next) { case 0: return this.api = Jr.a.create({ baseURL: this.baseURL, headers: { "Content-Type": "application/json" }, cancelToken: new Xr.CancelToken((function(e) { r.source = e })), timeout: 6e4 }), e.next = 3, new Promise((function(e) { chrome.storage.local.get("token", (function(t) { e(t) })) })); case 3: t = e.sent, (n = t.token) && this.api.setHeader("Extension-User-Token", n), this.api.setHeader("Extension-Token", "sdfkljsbdfgoipoasji2318743460r9edlfgjhpre896544534854368tfd34lfgkjbpfgepofdgsoidfsopdfjspodfjsidfjgphjmweorwjeoir5487623423jkdflgdkf874538953459834"); case 7: case "end": return e.stop() } }), e, this) }))); return function() { return e.apply(this, arguments) } }()}, { key: "cancelAllRequest", value: function() { e.instance.source() }}, { key: "createInterceptor", value: function() { this.interceptors = this.api.axiosInstance.interceptors }}]), e}Navigation triggers a Shopify metadata request
Browsing to a site makes Dropship - Product Research check its origin via /meta.json.
We saw it request shop.tesla.com/meta.json after navigating there; the background code uses that response to decide if the site is a Shopify store.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You navigate to a website in a browser tab.
The confirmed test navigation was to https://shop.tesla.com.
The extension requests that site's /meta.json file to check for Shopify metadata.
If the response reports published products, the extension records the tab's origin and Shopify domain for later UI updates.
Observed after navigating to https://shop.tesla.com; the claim evidence did not record a request body for this GET request.
- Visited site originhttps://shop.tesla.com
Shows which website you opened so the extension can decide whether that site is a Shopify store.
- Metadata URLhttps://shop.tesla.com/meta.json
Adds a predictable Shopify metadata path to the site you visited and requests it automatically.
- Published product countpublished_products_count: 42
Lets the extension decide whether to treat the current site as a Shopify store with products.
- Shopify store domainmyshopify_domain: tesla-shop.myshopify.com
Lets the extension associate the browser tab with a Shopify store domain after the metadata response is parsed.
The navigation listener builds the /meta.json request from the active tab origin
Fetch helper in the deobfuscated bundle
deobfuscated/app/background.jsasync function x(e) { return !o.some(t => e.includes(t)) && await fetch(`${e}/meta.json`).then(e => e.json()).catch(() => !1)}Navigation listener in the deobfuscated bundle
deobfuscated/app/background.jschrome.tabs.onUpdated.addListener(async function(e, { status: t}) { let n, [r] = await chrome.tabs.query({ active: !0, currentWindow: !0 }), a = r?.url ? new URL(r?.url)?.pathname : "", s = a?.includes("admin"), i = await U(); if (i?.id && (V(i), r?.url && ("complete" === t || "loading" !== t) && !s)) { if (n = new URL(r?.url).origin, o.some(e => n.includes(e)) || l.some(e => e?.id === r?.id)) return; if (f.some(e => e?.id === r?.id && e?.url === n)) return; let t = await x(n); t?.published_products_count ? (f.some(e => e?.id === r?.id) && (f = [...f.filter(t => t?.id !== e)]), l.some(e => e?.id === r?.id) || l.push({ id: r?.id, url: n, myshopify_domain: t?.myshopify_domain })) : (chrome.tabs.sendMessage(r.id, { message: "hide_extension" }), f.some(e => e?.id === r?.id) ? f.some(e => e?.id === r?.id) && (f = f.map(e => e?.id === r?.id ? { id: r?.id, url: n } : { ...e })) : f.push({ id: r?.id, url: n })) }})- shop.tesla.com
Received the observed /meta.json GET request after navigation to that origin.
- www.dropship.io
Also appeared in the verification evidence as a /meta.json probe during the same analysis.