Is Flipshope: Price Tracker and much more safe?
Flipshope is high risk. On a supported product or search page, e.g. Amazon or Flipkart, Flipshope reads CSS selectors from a config at data.flipshope.com, pulls title, price, image URLs, and page URL, and uploads that plus a per-install ID to data.flipshope.com.…
Who publishes itTechnosense Media Pvt. Ltd - no other listings under this identity
Technosense Media Pvt. Ltd - no other listings under this identity
What this publisher told the store about itself, and the other listings that told it the same thing.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Product pages and search results scraped to data.flipshope.com
On a supported product or search page, e.g.
Amazon or Flipkart, Flipshope reads CSS selectors from a config at data.flipshope.com, pulls title, price, image URLs, and page URL, and uploads that plus a per-install ID to data.flipshope.com.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You open a product page or a search-results page on a shopping site that matches Flipshope's remote store configuration, such as Amazon or Flipkart.
The page would normally load with no extra network activity beyond the store's own site.
The content script scrapes the product title, price, image URLs, and page URL from the page using selectors it downloaded from data.flipshope.com, then uploads them.
A single product page is uploaded to data.flipshope.com/api/products/; a listing page with multiple products (e.g. search results) is uploaded to data.flipshope.com/api/graph/, both tagged with a per-install identifier.
- Product page URLhttps://www.amazon.in/dp/B082LZGK39/
Identifies exactly which product or listing page you were viewing.
- Product IDB082LZGK39
The store's internal identifier for the exact item on the page.
- Product titleAmbrane Unbreakable 60W Fast Charging Type-C Cable
The full name of the product or listing you viewed.
- Price₹109
The price shown on the page at the time you viewed it.
- Product image URLshttps://m.media-amazon.com/images/I/71ExampleABC.jpg
Links to every product image shown on the page.
- Per-install identifier (extid)A7kP2qLs
Lets data.flipshope.com tie every upload back to your specific browser install over time.
Background handler that uploads scraped page data
sendpairs / sendprods message handlers
background.bundle.jscase "sendpairs": p.pairs && ((extid, site, pairs) => { he(u + "graph/", { store: site, data: pairs, extid, v: 2 }, 2, 0); })(r.myid, p.site, p.pairs); break;case "sendprods": p.prod_data && ((extid, site, prodData) => { he(u + "products/", { store: site, data: prodData, extid, v: 2 }, 2, 0); })(r.myid, p.site, p.prod_data); break;Content script loads remote selectors and applies them
contentScript.bundle.jschrome.storage.local.get("graph_base", async (stored) => { let config = stored && stored.graph_base; if (!(currentSite === "shopsy.in" || (config && config[currentSite]))) return; async function applySelectors() { let siteEntries = (config && config[currentSite]) || null; let match = siteEntries ? siteEntries.find((entry) => new RegExp(entry.must_path).test(window.location.href)) : null; if (!match && currentSite === "shopsy.in") { match = { site_id: 17, must_path: ".*", pg_data_sel: "[]", prod_data_sel: "{}" }; } if (match) { activeConfig = match; try { let listingSelectors = JSON.parse(activeConfig.pg_data_sel); scrapeListingFields(activeConfig.site_id, listingSelectors, 0); } catch (e) {} try { window.pd_sels = activeConfig.prod_data_sel; pd_sels = JSON.parse(activeConfig.prod_data_sel); let isTracked = await isProductTracked(activeConfig.site_id, window.location.href); if (isTracked) { renderTrackingUI(activeConfig, isTracked); setTimeout(() => scrapeProductFields(activeConfig.site_id, pd_sels, 0), 500); } } catch (e) {} } } applySelectors();});{ "store": 2, "data": { "pid": "B082LZGK39", "price": 109, "title": "Ambrane Unbreakable 60W Fast Charging Type-C Cable (illustrative — the evidence log recorded this field as a truncated summary, not the literal captured string)", "all_imgurls": [ "(illustrative — an image-URL array was present in the captured body; the evidence log did not record each URL individually)" ] }, "extid": "A7kP2qLs", "v": 2}- data.flipshope.com
Flipshope's own backend. Receives scraped product/listing data via POST to /api/products/ and /api/graph/, and serves the CSS selector config via /api/getsiteaffdata.
The CSS selectors that decide which page elements get scraped are fetched from data.flipshope.com's remote config rather than shipped fixed in the extension. That means the set of fields collected, or the set of sites covered, can change server-side without a new extension version and without further code review of what gets scraped.
Store clicks route through thebestdeals.app first
Clicking an outbound store link on a site in Flipshope's affiliate config blocks the click, sending the browser to thebestdeals.app/api/d first.
The URL includes the destination in dl and may include the stored extid before forwarding.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-506
- Source
- Dynamic sandbox
You click a product or store link on a shopping site that is active in Flipshope's affiliate configuration.
The page would normally follow the anchor's href directly.
The extension stops the normal click and routes the browser through thebestdeals.app/api/d first.
The original href is encoded into the dl query parameter before navigation.
- Clicked destinationhttps://www.amazon.in/dp/B0C6HSSDZK?ref=sr_1_1
Shows the remote endpoint the exact link you tried to open.
- Extension instance IDA7kP2qLs
Lets the endpoint group the click with the same browser profile when this value is present.
- Inventory labelext
Marks the redirect as coming from the extension channel.
- Source labelext_product_page
Marks the click path as a product-page redirect from the extension.
The click handler that replaces the destination URL
Content script click interception
contentScript.bundle.js(async () => { try { (await pd({ dataMSg: { msg: "check_if_attach_clickEvent", data: window.location.href } }))?.active && function () { try { async function e(t) { let n, i, a = null; try { if (t.defaultPrevented || t.button !== 0) return; if (i = t.target.closest("a"), !i || !i.href) return; t.preventDefault(); const o = new URL(i.href); n = o.href; if (i.target === "_blank") a = window.open("", "_blank"); const s = await async function (e) { try { const t = e; let n = Es(t); const i = await Hs("get", "validClickLinks") || {}; if (!i?.[n] || !i[n].active) return 0; let a = await Hs("get", "graph_base") || {}; if (!a[n]) return 1; let o = a[n]?.site_id; return await Ts(parseInt(o), t) ? 1 : 0; } catch (e) { return 0; } }(o.href); const r = Es(o.href), l = await Hs("get", "linkActivationTime") || {}, c = l[r], d = !c || +new Date > c; if (s && d) { const e = "https://thebestdeals.app/api/d?dl=", t = await Hs("get", "extid"), n = e + encodeURIComponent(o.href) + (t ? "&extid=" + t : "") + "&inventory=ext&source=ext_product_page"; !function (e, t, n) { try { e === "_blank" ? n && !n.closed ? (n.location.href = t, n.focus()) : window.open(t, "_blank") : window.location.href = t; } catch (e) {} }(i.target, n, a); l[r] = +new Date + 18e5; Hs("set", { linkActivationTime: l }); } else { a && !a.closed ? (a.location.href = o.href, a.focus()) : (document.removeEventListener("click", e, true), i.click(), setTimeout(() => { document.addEventListener("click", e, true); }, 0)); } } catch (t) { a && !a.closed && n ? (a.location.href = n, a.focus()) : i ? (document.removeEventListener("click", e, true), i.click(), setTimeout(() => { document.addEventListener("click", e, true); }, 0)) : n && (window.location.href = n); } } document.addEventListener("click", e, true); } catch (e) {} }(); } catch (e) {}})();Background active-store configuration
background.bundle.jsasync function Z() { const e = function () { const e = navigator.userAgent || ""; return e.includes("Edg/") ? "edge" : e.includes("OPR/") || e.includes("Opera") ? "opera" : e.includes("Firefox/") ? "firefox" : e.includes("Chrome/") ? "chrome" : "unknown"; }(); K[e] || (K[e] = {}); const a = K[e], t = `https://data.flipshope.com/api/getsiteaffdata?browser=${e}`; let n = await X(a, t, x, `${p}${e}`), o = {}; n.data.forEach((e => { o[e.store_name.toLowerCase()] = { active: e.active }; })); return o;}Every 30 minutes
After one redirected click for a store, the extension records a 30-minute cooldown before redirecting another click for that store.
- thebestdeals.app
Receives the redirected click URL with dl, optional extid, inventory, and source query parameters.
- data.flipshope.com
Provides the active affiliate-store list used to decide whether the click listener should be attached.
Flight search itineraries sent to Flipshope with a persistent device ID
On booking sites like MakeMyTrip, Goibibo, Cleartrip, ixigo, Flipshope reads origin, destination, dates, passengers, cabin class from the URL, sends the itinerary and a per-install ID to data.flipshope.com; Cleartrip triggered two POSTs.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You search for a flight on a supported booking site.
e.g. a Cleartrip results page with origin, destination, dates, and passenger counts in the URL.
The extension parses that itinerary out of the URL and sends it to its own server, tagged with your persistent extension ID.
This happens automatically on page load, with no click or prompt from you.
Observed during dynamic analysis: navigating to a Cleartrip flight-results URL (DEL to BOM) triggered two POSTs matching this body exactly.
- Content-Type
- application/json
{ "from": "DEL", "to": "BOM", "date": { "day": 15, "month": 9, "year": 2026 }, "returnDate": { "day": null, "month": null, "year": null }, "adult": 1, "cabinClass": "CABIN_CLASS_ECONOMY", "extensionId": "eXbk89DZ"}- Origin airportDEL
Identifies where you're flying from.
- Destination airportBOM
Identifies where you're flying to.
- Travel dates2026-09-15
Reveals your planned departure (and return, if round-trip) dates.
- Passenger count1 adult
Shows how many adults, children, and infants are on the booking.
- Cabin classCABIN_CLASS_ECONOMY
Shows whether you're searching economy, premium, business, or first class.
- Persistent extension IDeXbk89DZ
A per-install ID stored as a cookie that ties this and every future search back to the same install.
Parsing a Cleartrip flight-results URL and posting the itinerary
Readable equivalent
// case for the URL scheme resolved server-side as "lmcleotkxx" (a Cleartrip-style booking URL)case "lmcleotkxx": { const from = url.match(/from=([^&]+)/)?.[1] ?? null; const to = url.match(/to=([^&]+)/)?.[1] ?? null; const departDate = decodeURIComponent(url.match(/depart_date=([^&]+)/)?.[1] ?? ""); const [day, month, year] = departDate.split("/"); const adults = parseInt(url.match(/adults=([^&]+)/)?.[1] ?? "0", 10); const cabinClassCode = url.match(/class=([^&]+)/)?.[1] ?? null; if (!(from && to && day && month && year)) return false; // ... maps cabinClassCode to CABIN_CLASS_ECONOMY / _BUSINESS / _FIRST ...}// after parsing, in the caller:itinerary.extensionId = await getPersistentExtensionId();const response = await fetch("https://data.flipshope.com/api/flight_fare", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(itinerary)});- data.flipshope.com
Flipshope's own backend. Receives the parsed itinerary and persistent extension ID, and returns fare comparison data shown back to you in the extension UI.