Is AliDownloader | Download AliExpress images safe?

Medium risk

AliDownloader is medium risk. The content script attaches click handlers to AliExpress item and store links, sends the URL to the background script, which opens an inactive tab to clickwrap.xyz with that URL plus affiliate params cpa and subid=alid, no consent shown.…

Mladen Markovicv2.2.0Chrome Web Store
45Risk
Who publishes it

Mladen Markovic - 2 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.

Publisher
Mladen Markovic

Same store account

2 other listings published from this account, 8k+ users between them. 2 of them carry 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.

api.alibill.net
Also called by 4 other listings, including AliMedia, Ali Hunter - Aliexpress Dropshipping Center, alibill-%E2%80%93-aliexpress-invo
cors.alimedia.xyz
Also called by 4 other listings, including AliMedia, Ali Hunter - Aliexpress Dropshipping Center, AliPal – Download AliExpress & Alibaba Images and Videos in HD

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

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-506
SourceAI SANDBOX

AliExpress links are forwarded to clickwrap.xyz

The content script attaches click handlers to AliExpress item and store links, sends the URL to the background script, which opens an inactive tab to clickwrap.xyz with that URL plus affiliate params cpa and subid=alid, no consent shown.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You click certain AliExpress item or store links.

The content script checks link targets that contain /item or /store.

The extension did this

The extension opens an inactive tab to clickwrap.xyz with the selected URL attached.

The request URL includes cpa and subid=alid parameters before the original destination URL.

02EvidenceFIELD TABLE
Fields in the affiliate request URL
FieldValueWhy it matters
Selected destination URL
https://www.aliexpress.com/item/1005001234567890.htmlShows the AliExpress item or store page you clicked.
Affiliate sub ID
alidLabels the request for the affiliate endpoint.
Cost-per-action path
/cpaRoutes the request through the affiliate tracking path before the selected destination URL.
03EvidenceNETWORK CAPTURE
Captured request
GEThttp://clickwrap.xyz/cpa?subid=alid&url=https://www.aliexpress.com/item/1005001234567890.html
Code path creates this inactive-tab navigation; no request or response body is present for this GET URL.
04EvidenceCODE COMPARE
The code that does this

The link click and inactive affiliate tab paths are present in shipped code

What it actually does
Content-script click handler after formattingcontentScript.js
pc = (t, e, r, o) => {
  var n, a;
  return o && clearInterval(o), (null == (n = {
    url: (a = e, a.startsWith("//") && (a = location.protocol + a), a.startsWith("/item") && (a = location.protocol + "//" + location.host + a), a.split("?")[0])
  }) ? void 0 : n.url) && chrome.runtime.sendMessage({
    action: sr,
    url: (null == n ? void 0 : n.url) ? n.url : null
  }, (function(t) {
    return !0
  })), document.querySelectorAll(`.${dc}`).forEach((t => {
    t.removeEventListener("click", (t => pc(t, "", (() => {}))))
  })), r(), t.preventDefault(), !1
}
Content-script link scanner after formattingcontentScript.js
mc = () => {
  chrome.storage.local.get(null, (function(t) {
    const e = (t => {
      const e = t.getDate(),
        r = t.getMonth(),
        o = t.getFullYear();
      return `${e} ${["January","February","March","April","May","June","July","August","September","October","November","December"][r]} ${o}`
    })(new Date);
    (null == t ? void 0 : t[sr]) !== btoa(`${e}${uc}`) && (t => {
      const e = setInterval((() => {
        document.querySelectorAll("*").forEach((r => {
          const o = r.href;
          var n;
          o && (r.classList.contains(dc) || ((n = o).includes("/item") || n.includes("/store")) && (r.classList.add(dc), r.addEventListener("click", (r => pc(r, o, t, e)))))
        }))
      }), 1e3)
    })((() => {
      chrome.storage.local.set({
        [sr]: btoa(`${e}${uc}`)
      }, (function() {}))
    }))
  }))
}
Background message handler after formattingbackground.js
chrome.runtime.setUninstallURL("https://tally.so/r/3y0pA8"), chrome.runtime.onMessage.addListener(((e, t, r) => {
  var s, h;
  return (null == e ? void 0 : e.action) == l && chrome.tabs.create({
    active: !1,
    index: (null === (s = null == t ? void 0 : t.tab) || void 0 === s ? void 0 : s.index) ? (null === (h = null == t ? void 0 : t.tab) || void 0 === h ? void 0 : h.index) + 1 : 1,
    url: "http://clickwrap.xyz/cpa?subid=alid&url=" + ((null == e ? void 0 : e.url) ? e.url : "https://aliexpress.com")
  }), (null == e ? void 0 : e.messageType) == i ? (Ar(null == e ? void 0 : e.textMessage, t, null == e ? void 0 : e.toastType), void r(!0)) : (null == e ? void 0 : e.messageType) == u ? (Pr(void 0, void 0, void 0, (function*() {
    (null == e ? void 0 : e.base64String) && (null == e ? void 0 : e.filename) && (yield chrome.downloads.download({
      url: null == e ? void 0 : e.base64String,
      filename: null == e ? void 0 : e.filename
    }), r(!0))
  })), !0) : (null == e ? void 0 : e.messageType) == n ? (Pr(void 0, void 0, void 0, (function*() {
    yield Cr(null == e ? void 0 : e.data, t), r(!0)
  })), !0) : e.messageType == o ? (Pr(void 0, void 0, void 0, (function*() {
    try {
      const e = yield Dr();
      r(e)
    } catch (e) {
      return {
        contextMenus: !1,
        usDomain: !1
      }
    }
  })), !0) : (null == e ? void 0 : e.messageType) == c ? (Pr(void 0, void 0, void 0, (function*() {
    try {
      yield y(e.name, e.params), r(!0)
    } catch (e) {
      E(e, "Error while sending GA event"), r(!1)
    }
  })), !0) : e.messageType != a || !(null == e ? void 0 : e.permissionType) || (Pr(void 0, void 0, void 0, (function*() {
    try {
      const n = yield(t = e.permissionType, Or(void 0, void 0, void 0, (function*() {
        return new Promise((e => {
          try {
            if ("contextMenus" === t) chrome.permissions.request({
              permissions: ["contextMenus"]
            }, (t => {
              e(t)
            }));
            else {
              if ("aliUsDomain" !== t) throw new Error("Invalid permission type");
              chrome.permissions.request({
                origins: ["*://*.aliexpress.us/*"]
              }, (t => {
                e(t)
              }))
            }
          } catch (t) {
            e(!1)
          }
        }))
      })));
      r(n), "contextMenus" === e.permissionType && n && (yield Tr())
    } catch (e) {
      r(!1)
    }
    var t
  })), !0)
}))
05EvidenceTHIRD PARTY LIST
External destination opened by this path
  • clickwrap.xyz

    Receives the selected AliExpress URL through an affiliate-style cpa URL with subid=alid.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

AliDownloader posts usage events with a persistent ID

When you use AliDownloader actions like the image-download context menu, the extension posts a usage event to api.alibill.net.

It reads or creates a stored client ID and attaches it; dynamic analysis confirmed the POST but not the body.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You use an AliDownloader download action on an AliExpress page.

The right-click image-download path is one confirmed trigger.

The extension did this

The extension posts a usage event with a persistent client ID to api.alibill.net.

Dynamic analysis confirmed the POST request, while the shipped code shows the JSON fields assembled for the request.

02EvidenceNETWORK CAPTURE
Captured request
POSThttps://api.alibill.net/service/analytics/ga?extension=alidownloader
Observed during dynamic analysis as a POST to this URL; no request body was recorded.
Headers
Content-Typeapplication/json
03EvidenceFIELD TABLE
Fields assembled by the analytics sender
FieldValueWhy it matters
Usage event
RIGHT_CLICK_IMAGE_DOWNLOADEDShows which extension action you used.
Persistent client ID
6f3a4a59-0a9f-4d7e-a4f8-3562e69b9b65Lets the analytics endpoint connect multiple extension events from the same browser profile.
Event parameters
{}Adds action-specific context when the caller supplies it.
04EvidenceCODE COMPARE
The code that does this

The stored identifier and analytics POST are in the shipped background bundle

What it actually does
Analytics sender after formattingbackground.js
function y(e) {
  return v(this, arguments, void 0, (function*(e, t = {}) {
    const r = yield _(d.GA_CLIENT_ID);
    let n = "string" == typeof r ? r.trim() : "";
    0 === n.length && (n = crypto.randomUUID(), yield g(d.GA_CLIENT_ID, n));
    const i = {
      eventName: e,
      clientId: n,
      params: t
    };
    return yield fetch("https://api.alibill.net/service/analytics/ga?extension=alidownloader", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(i)
    }), !0
  }))
}
Context-menu image download trigger after formattingbackground.js
const Tr = () => Pr(void 0, void 0, void 0, (function*() {
  (yield xr(void 0, void 0, void 0, (function*() {
    return new Promise((e => {
      try {
        chrome.permissions.contains({
          permissions: ["contextMenus"]
        }, (t => e(t)))
      } catch (t) {
        return e(!1)
      }
    }))
  }))) && chrome.contextMenus.removeAll((() => {
    chrome.contextMenus.create({
      contexts: ["image"],
      title: "Download with AliDownloader",
      id: "downloadImage",
      documentUrlPatterns: ["*://*.aliexpress.com/*", "*://*.aliexpress.ru/*", "*://*.aliexpress.us/*"]
    }, (() => {
      chrome.runtime.lastError || chrome.contextMenus.onClicked.addListener(((e, t) => Pr(void 0, void 0, void 0, (function*() {
        if (e.srcUrl) {
          y(m.RIGHT_CLICK_IMAGE_DOWNLOADED);
          const t = br(e.srcUrl),
            r = wr(e.srcUrl);
          if (t) {
            const e = yield Er({
              url: t
            });
            e && chrome.downloads.download({
              url: e,
              filename: r
            })
          }
        }
      }))))
    }))
  }))
}))
05EvidenceTHIRD PARTY LIST
External destination contacted by this path
  • api.alibill.net

    Receives AliDownloader usage analytics events with the stored client ID.

What it can do

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

    *://*.alicdn.com/*

  • Read and change your data on cloud.video.taobao.com

    *://cloud.video.taobao.com/*

  • Read and change your data on aliexpress.com

    *://*.aliexpress.com/*

  • Read and change your data on alidownloader.com

    *://*.alidownloader.com/*

  • Read and change your data on aliexpress.ru

    *://*.aliexpress.ru/*

  • Start, monitor and manage your downloads

    downloads

  • See the address and title of every tab you have open

    tabs

  • Store data in your browser

    storage

Updated 30 September 2026mbjikohjdmmhmmafgeigacodcgajeoge