Is Shopee Save - Download Product Images & Video safe?
Shopee Save is high risk. Opening a Shopee product page has the extension POST your Shopee origin and product path to imgvidcfig.com/api/shopy, which returns a redirect URL. Your tab briefly loads imgvidcfig.com with that URL in the query string, then returns.…
Who publishes itE-Media - 1 other listing from the same operator, none carrying a finding
E-Media - 1 other listing from the same operator, none carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
1 other listing published from this account, 5k+ users between them, none of them carrying a finding.
Shared hosts - 2 hostnames
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
Shopee Product Pages Routed Through imgvidcfig.com Before Loading
Opening a Shopee product page has the extension POST your Shopee origin and product path to imgvidcfig.com/api/shopy, which returns a redirect URL.
Your tab briefly loads imgvidcfig.com with that URL in the query string, then returns.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You open any Shopee product page.
The extension sends the product URL path and your Shopee origin to imgvidcfig.com before the page loads.
Your browser tab is redirected through an imgvidcfig.com URL containing the encoded original product URL, then bounced back to Shopee, all within milliseconds.
HTTP 200. Response headers include keepurl: https://imgvidcfig.com/r?ikuc=%2Fshopee.com%2Fproduct-i.111.222
- uuid
- /CANARY_BIRD_12345_product-i.111.222
- source
- https://shopee.com
- content-type
- application/json
- Product page path/CANARY_BIRD_12345_product-i.111.222
The exact Shopee product path you navigated to, identifying which product you viewed.
- Your Shopee regional domainhttps://shopee.com
Which Shopee country site you use (e.g. shopee.com, shopee.sg, shopee.vn).
The interception and redirect code, from the extension's shipping source.
POST to imgvidcfig.com (the _e function)
const pendingRedirects = {}; // ye: pathname → redirect URL mapconst visitedPaths = []; // ve: dedup guardconst API_BASE = 'https://imgvidcfig.com/api'; // geasync function notifyServer(productPath, shopeeOrigin) { // Skip if we've already logged this path if (Object.keys(pendingRedirects).includes(productPath)) return; const endpoint = `${API_BASE}/shopy`; const response = await axios({ method: 'POST', url: endpoint, headers: { 'content-type': 'application/json', source: shopeeOrigin, // e.g. 'https://shopee.com' uuid: productPath // e.g. '/product-i.111.222' } }); const keepUrl = response.headers.get('keepurl'); if (keepUrl) { // Store redirect URL keyed by product path Object.assign(pendingRedirects, { [productPath]: keepUrl }); }}Navigation interception + redirect
chrome.webRequest.onBeforeRequest.addListener((event) => { if (isShopeeProductUrl(event.url)) { const path = new URL(event.url).pathname; // Trigger the server POST (once per path, with 15s cooldown) if (!visitedPaths.includes(path)) { visitedPaths.push(path); notifyServer(path, new URL(event.url).origin); setTimeout(() => { visitedPaths.splice(visitedPaths.indexOf(path), 1); }, 15000); } // If a redirect URL came back from the server, redirect the tab through it const pendingPaths = Object.keys(pendingRedirects); if (pendingPaths.length > 0) { const path = pendingPaths[0]; const redirectUrl = new URL(pendingRedirects[path]); const currentUrl = new URL(event.url); // Embed the original Shopee URL in the redirect redirectUrl.searchParams.set('ikuc', encodeURIComponent(currentUrl.href)); delete pendingRedirects[path]; chrome.tabs.update(event.tabId, { url: redirectUrl.href }); } }}, { urls: ['<all_urls>'], types: ['main_frame'] });Return bounce: redirects back to Shopee once imgvidcfig.com responds
chrome.webRequest.onHeadersReceived.addListener((event) => { const ikuc = new URL(event.url).searchParams.get('ikuc'); if (ikuc) { // The tab is currently on imgvidcfig.com — bounce it back to the original Shopee URL const originalUrl = new URL(decodeURIComponent(ikuc)); chrome.tabs.update(event.tabId, { url: originalUrl.href }); }}, { urls: ['<all_urls>'], types: ['main_frame'] });- imgvidcfig.com
Receives a POST for every Shopee product page opened. Ownership not publicly disclosed; domain registration is privacy-protected.
Remote server writes arbitrary keys into Shopee Save's extension storage
Opening a Shopee product page has the background script query imgvidcfig.com and copy the response's `prefix` field into local storage, unfiltered.
One response set keys like `__POSITION_DISPLAY`, then a content script read that value back.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
You open a Shopee product page.
Any shopee.* domain with a product-ID path pattern in the URL triggers this.
The extension asks imgvidcfig.com for configuration, then writes the server's answer directly into its own local storage.
No allowlist restricts which keys the response is allowed to set.
HTTP 200. Body: {"data":{"prefix":{"__POSITION_DISPLAY":".page-product.container","__SELECTOR_TITLE":"._44qnta span"...}}}, observed during dynamic analysis on a live Shopee product-page visit.
- uuid
- /123456789/1046680173
- source
- https://shopee.co.th
- content-type
- application/json
background.js, the POST and the unchecked storage write
_e() — POSTs to imgvidcfig.com, then writes the response's prefix object to storage unchecked
assets/js/background.js:5705-5742 function _e() { return (_e = o()(i.a.mark((function e(t, n) { var r, a, o, s, u, l; return i.a.wrap((function(e) { for (;;) switch (e.prev = e.next) { case 0: if (e.prev = 0, !Object.keys(ye).find((function(e) { return e === t }))) { e.next = 5; break } return e.abrupt("return"); case 5: return o = "".concat(ge, "/shopy"), e.next = 8, h()({ method: "POST", url: o, adapter: w, headers: { "content-type": "application/json", source: n, uuid: t } }); case 8: s = e.sent, u = null == s || null === (r = s.headers) || void 0 === r ? void 0 : r.get("keepurl"), l = (null === (a = s.data) || void 0 === a ? void 0 : a.prefix) || {}, u && (D(c()({}, T, u)), Object.assign(ye, c()({}, t, u))), D(me({}, l)), e.next = 17; break; case 15: e.prev = 15, e.t0 = e.catch(0); case 17: case "end": return e.stop() } }), e, null, [ [0, 15] ]) })))).apply(this, arguments) }D() / M() / me() — D(me({}, prefix)) resolves to a bare chrome.storage.local.set(prefix) call
assets/js/background.jsfunction D(e) { return M.apply(this, arguments)}function M() { return (M = o()(i.a.mark((function e(t) { return i.a.wrap((function(e) { for (;;) switch (e.prev = e.next) { case 0: return e.abrupt("return", new Promise((function(e) { chrome.storage.local.set(t, (function() { return e(!0) })) }))); case 1: case "end": return e.stop() } }), e) })))).apply(this, arguments)}function me(e) { for (var t = 1; t < arguments.length; t++) { var n = null != arguments[t] ? arguments[t] : {}; t % 2 ? pe(Object(n), !0).forEach((function(t) { c()(e, t, n[t]) })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : pe(Object(n)).forEach((function(t) { Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(n, t)) })) } return e}- Page container selector__POSITION_DISPLAY: ".page-product .container"
A CSS selector, supplied by the server rather than shipped in code, used to locate the product image/title block.
- Product title selector__SELECTOR_TITLE: "._44qnta span"
A CSS selector for the product title element, likewise sent by imgvidcfig.com rather than hardcoded in the extension.
- imgvidcfig.com
Receives a POST on every Shopee product-page visit and returns a 'prefix' object that the extension writes into its own local storage unchecked. Not a Shopee or Chrome domain.
Content Scripts Injected Into Every Website, Not Just Shopee
The extension's stated job is downloading Shopee images and videos, but its manifest runs content scripts on every site. vendors.js and content_scripts.js inject everywhere at document_end, reaching banking, email, social sites.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You visit any website, including banking sites, email, and social media.
The extension's content scripts are injected into the page and gain read/write access to its DOM, form inputs, and page content.
This occurs on every HTTP/HTTPS page, not only on Shopee. The extension's stated purpose is Shopee image/video downloading, which does not require access to non-Shopee pages.
The content_scripts declaration in manifest.json vs. what a Shopee-only scope would look like.
What a Shopee-scoped content_scripts block would look like
// A content_scripts block scoped to Shopee only:"content_scripts": [ { "matches": [ "*://*.shopee.com/*", "*://*.shopee.sg/*", "*://*.shopee.vn/*", "*://*.shopee.co.id/*", "*://*.shopee.ph/*", "*://*.shopee.com.my/*", "*://*.shopee.co.th/*", "*://*.shopee.tw/*" ], "js": [ "assets/js/vendors.js", "assets/js/content_scripts.js" ], "css": [ "assets/css/app.css" ], "run_at": "document_end" }]// The actual extension uses '<all_urls>' instead, covering every website.- Form inputs on any sitePassword field on your bank's login page
Content scripts can read text typed into any form field, including login pages, banking forms, and search boxes, on any website you visit.
- Full page DOMEmail inbox content on Gmail, account numbers on banking site
The injected scripts can read and modify the entire HTML structure of any page, including content not visible to the user.
- Page URL and navigation contexthttps://mail.google.com/mail/u/0/#inbox
Content scripts run after the page loads and have access to the current URL and document location on every site.
The broad injection scope grants capability, not confirmed abuse. Our dynamic analysis confirmed content scripts load on non-Shopee origins (vendor and content script resources were observed loading with source='cs' on shopee.com, and extension-related DOM elements were observed on Google). We did not observe the extension reading or transmitting page content from non-Shopee sites in our test sessions. However, the code has the DOM access to do so on every page you visit.
What it can do
Permissions this extension asks for, as declared in version 3.0.21. 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 site you visit
<all_urls>
Store data in your browser
storage
Watch every request your browser makes
webRequest
Start, monitor and manage your downloads
downloads
Add items to the right-click menu
contextMenus