Is AiPrice(AliPrice) Sourcing Tool safe?

Clean risk

This extension adds image-based product search, price history, and sourcing tools for major shopping platforms.

It lets users search for similar products from an image, zoom product images, download product images and videos, copy product links and titles, and export store products to Excel. It also shows price and order history on supported marketplaces and adds Amazon listing data such as rankings, ASIN, Amazon seller status, and FBA seller counts.

aiprice2v4.1.6Chrome Web Store
7Risk
Who publishes it

aiprice2 - 14 other listings from the same operator, 2 of them carrying a finding

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.

t.aiprice.com
Also called by 8 other listings

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

Publishers can request a review.

Findings

Screenshot translate sends image data and page URL

Triggering AiPrice(AliPrice)'s screenshot-to-translate captures the tab, crops the selection, and sends an OCR request with the JPEG data, languages, and page URL to api.aiprice.com/chrome/translate/baiduOcrIdentifyWords.

No body recorded.

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

You trigger screenshot translation from the extension.

The default shortcut is Ctrl+Shift+3 on Windows and Command+Shift+3 on macOS.

The extension did this

The extension captures the visible tab, crops your selected area, and sends it with the page address for OCR.

The request fields are constructed as imgBase64, sourceLang, targetLang, and url.

Fields built for the OCR request
  • Selected screenshot area
    data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxISEhUSEhIVFRUVFRUVFRUVFRUVFRUVFRUWFhUVFRUYHSggGBolGxUVITEhJSkrLi4uFx8zODMtNygtLisBCgoKDg0OGxAQGzAmICYtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLf/AABEIAAEAAQMBIgACEQEDEQH/xAAXAAEBAQEAAAAAAAAAAAAAAAAAAQID/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/EABYBAQEBAAAAAAAAAAAAAAAAAAABAv/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AM8A/9k= (illustrative)

    Contains the visible page content you selected for translation, which can include product text, messages, or other on-screen details.

  • Current page URL
    https://detail.1688.com/offer/732418905640.html

    Ties the screenshot translation request to the page you were viewing.

  • Source language
    auto

    Shows the language the OCR workflow should read from the screenshot.

  • Target language
    en

    Shows the language selected for the translated OCR result.

Captured request
POSThttps://api.aiprice.com/chrome/translate/baiduOcrIdentifyWords

The request body was not recorded during traffic capture; the method, URL, and fields are from the shipped request builder.

The code that does this

The shortcut sends the screenshot message to the content script

Readable version

Readable shortcut command

assets/js/background.js:56207-56223
const t = "Ctrl+Shift+3",  a = "Command+Shift+3",  r = {    name: "screenshotToTranslate",    command: {      suggested_key: {        default: t,        mac: a      },      description: "__MSG_context_menus_translator__"    },    handler: {      type: "client/translator/screenshot",      payload: {        trigger: "screenshot_shortcut"      }    }  }

Readable screenshot capture route

assets/js/background.js:11325-11341
jp = [{  route: "backend/tab/screenshot",  handler: function(e, t, a) {    return browser.tabs.captureVisibleTab(null, {      format: "jpeg",      quality: 100,      ...e    }).then((e => a(e ? (0, I.CW)("success", "", {      data: {        dataUrl: e      }    }) : (0, I.CW)("fail", "", {      data: {}    })))).catch((e => a((0, I.CW)("error", e && e.message ? e.message : "", {      err: e    }))))  }}]
The code that does this

The content script crops the JPEG and passes the page URL to OCR

Readable version

Readable OCR call from the translator flow

assets/js/content-script.js:99365-99391
_.indexOf("ocr") > -1 && b.unshift((async (e, t = {}) => {  let r = {};  try {    if (_.indexOf("screenshot") > -1 && "success" !== t?.status) throw new Error("no screenshot");    if (h(), this.process = "ocr", p = {        sl: "",        tl: "",        ...this.parseLanguageForAPI(a || "", i || "", !0)      }, d = {        sl: "",        tl: "",        ...this.parseLanguageForAPI(a || "", i || "", !1)      }, y("ocr", [`ocr size ${this.textOrImgDetails.rect.width}x${this.textOrImgDetails.rect.height}`].join("")), r = await (0, ui.p1)(null, {        data: {          imgBase64: u,          url: o,          sl: p.sl,          tl: p.tl        }      }), h(), 2001 === r.data.code) throw this.isLogedin = !1, new Error("required login");    if (0 !== r.data.code || !r.data.data) throw new Error(r.data.msg || "ocr fail");    if ("" === (0, ti.stripEmptyLines)((0, ti.stripSpaces)(r.data.data.words))) throw new Error("no words detected");    if (u = r.data.data.words, this.ocr.text = r.data.data.words, this.translation.originalText = r.data.data.words, y("translate", [`source language is ${"auto"===d.sl?d.slAuto.replace(/\(.*\)/,`(${r.data.data.sourceLang})`):d.sl}, `, `target language is ${r.data.data.targetLang}`].join("")), "" === (0, ti.stripEmptyLines)((0, ti.stripSpaces)(r.data.data.translateWords))) throw new Error("no translation");    return this.translation.sl = r.data.data.sourceLang, this.translation.tl = r.data.data.targetLang, this.translation.text = r.data.data.translateWords, e((0, ti.wrapResult)("success", "", {}))  } catch (t) {    return t.message.indexOf("ocr success") > -1 ? y("ocr", t.message) : y("ocr", "failed"), e((0, ti.wrapResult)("fail", t.message || "", {}))  }}))
The code that does this

The API wrapper maps image data and URL into the POST request

Readable version

Readable OCR request builder

assets/js/content-script.js:134006-134033
$e = (e, t) => ({  config: {    url: `${o.nz}/chrome/translate/baiduOcrIdentifyWords`,    method: "POST",    params: {      ...e.params || {}    },    data: {      imgBase64: e.data.imgBase64,      sourceLang: e.data.sl,      targetLang: e.data.tl,      url: e.data.url    }  },  options: t}),De = (e = $e, t = {}, a = {}) => {  let i = {    config: {},    options: {      appendAppInfoToHeader: !0,      cache: !1,      dataType: "json",      ...a || {}    }  };  return i = e ? e(t, i.options) : $e(t, i.options), i.options.encrypt = y(i.config.data), I(i.config, i.options)}

Readable AiPrice API base URL

assets/js/content-script.js:136749-136755
let i = "https://www.aiprice.com",  r = "https://api.aiprice.com",  s = "https://api-cn.aiprice.com",  o = "https://agent.aiprice.com",  n = "https://index.aiprice.com";const l = i,  c = r
OCR destination constructed by the extension
    • api.aiprice.com

    Receives the screenshot OCR POST with image data, page URL, and language settings.

Matomo analytics sends page URLs to AliPrice tracking

AiPrice(AliPrice)'s analytics path attaches the page URL to Matomo events.

A POST captured in testing went to matomo.chnprice.com with an event category, action, and encoded URL aiprice.com/resources/operate/doc/video.html?version=4.0.5.

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

You open or use an extension feature that starts its analytics flow.

The observed request was generated while the extension opened its operating-document page for version 4.0.5.

The extension did this

The extension sends a Matomo event that includes the page address as request context.

The request body included the event category, event action, and the encoded current URL.

Captured request
POSThttps://matomo.chnprice.com/matomo.php

Observed during dynamic analysis as a Matomo analytics POST.

Body
e_c=install_view&e_a=open&url=https%3A%2F%2Fwww.aiprice.com%2Fresources%2Foperate%2Fdoc%2Fvideo.html%3Fversion%3D4.0.5
Fields carried by the observed Matomo request
  • Current page URL
    https://www.aiprice.com/resources/operate/doc/video.html?version=4.0.5

    Shows the exact page open when the analytics event was recorded.

  • Event category
    install_view

    Shows which extension workflow the analytics system associated with the page visit.

  • Event action
    open

    Shows the user-facing event step recorded with that page address.

The code that does this

The analytics code attaches the current page URL before sending the event

Readable version

Readable Matomo queue sender

assets/js/background.js:9077-9085
const kl = async (e = [], t = !1) => {  let a = !0;  if (a) {    let t = globalThis[Al] = globalThis[Al] || [];    navigator?.sendBeacon ? (El || (El = !0, t.push(["trackPageView"])), t.push(e)) : !Pl && browser?.runtime?.sendMessage ? browser.runtime.sendMessage({      type: Tl.push2paq,      payload: e    }) : Pl && "event" === e[0] && Dl.set(e)  }}

Readable event wrapper

assets/js/background.js:9157-9181
Ul = async (e = {}) => {  {    const {      event: t,      category: a,      action: r,      name: o,      value: i,      details: n,      ...s    } = {      event: "",      category: "",      action: "",      ...e || {}    };    let c = "trackEvent",      l = a || "",      u = r || "",      p = o || "",      d = i || "",      m = n || "",      h = [];    (0, I.Kg)(l) && (0, I.Kg)(u) && (kl(["setCustomDimension", 6, globalThis.location.href, "action"]), (0, I.Im)(m) || null === m || void 0 === m || kl(["setCustomDimension", 7, JSON.stringify(m), "action"]), h.push(l, u), (0, I.Kg)(p) && (h.push(p), (0, I.Et)(d) && h.push(d)), kl([c].concat(h)))  }}

Readable privacy-status gate

assets/js/background.js:9182-9191
Ml = async (e, t, a) => {  try {    const r = await R.F.get("settings");    if (r?.data?.privacyStatus && (Rl(e) && zl((() => Ol(e)), "[sendToAlipriceAnalyze] Error"), Nl(t) && zl((() => Ll(t)), "[sendToAlipriceStatistics] Error"), (0, I.Qd)(a))) {      let e = !1;      "labelAsUrl" in a && (e = a.labelAsUrl, delete a.labelAsUrl), zl((() => Fl(a, {        labelAsUrl: e      })), "[sendToGoogle] Error")    }  } catch (e) {}}
The code that does this

Default settings keep the analytics privacy switch enabled

Readable version

Readable settings migration

assets/js/background.js:13325-13329
r = async e => {  let t = (await R.F.get("settings")).data || {},    r = {};  "platforms" in t || (t = (0, I.qt)(a.defaultSettings || {})), r = (0, I.h1)({}, (0, I.qt)(a.defaultSettings || {}), (0, I.qt)(t || {})), (0, I.Tn)(a.migrateSettings) && (r = a.migrateSettings((0, I.qt)(r), (0, I.qt)(t || {}), (0, I.qt)(a.defaultSettings || {}))), "privacyStatus" in r && (r.privacyStatus = !0), zd({    installedInfo: e,    memoryCheck: a.memoryCheck,    settings: r  })}

Readable default setting

assets/js/background.js:30331-30335
Wv = () => ({  privacyConsentRequired: !1,  privacyStatus: !0,  privacyItemCookiesStatus: !0,  privacyItemFunctionalStatus: !0})
Observed analytics destination
    • matomo.chnprice.com

    Receives the Matomo event POST containing the page URL and event fields.

What it can do

Permissions this extension asks for, as declared in version 4.1.6. 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

    http://*/*

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

    https://*/*

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

    activeTab

  • Schedule its own background tasks

    alarms

  • Add items to the right-click menu

    contextMenus

  • Read and change cookies, including the ones that keep you signed in

    cookies

  • Block and redirect the requests your browser makes

    declarativeNetRequest

  • See which of your requests its blocking rules matched

    declarativeNetRequestFeedback

  • Show you desktop notifications

    notifications

  • Run its own code inside the pages you visit

    scripting

  • Store data in your browser

    storage

Updated 30 September 2026lenipkahddombkldhfmcnnjakmcepdlk