Is Alichrome - Free Invoice generator safe?
AliChrome is medium risk. Clicking Download PDF Invoice on an AliExpress order page makes the content script collect the shipping flag and currency, prepend them to an alert, and have the background worker send it to api.alichrome.io over HTTP, exposing the IP.
Who publishes itAliChrome - no other listings under this identity, 1 shared hostname
AliChrome - 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
Invoice country and currency sent over HTTP
Clicking Download PDF Invoice on an AliExpress order page makes the content script collect the shipping flag and currency, prepend them to an alert, and have the background worker send it to api.alichrome.io over HTTP, exposing the IP.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You click the Download PDF Invoice button on an AliExpress order detail page.
The extension adds this button next to the existing order-status controls.
The extension prefixes invoice-generation messages with your shipping country flag and currency, then sends them to api.alichrome.io over HTTP.
The background worker uses a GET request to the /alert/ path on that host.
- Shipping destination flagFR
This reveals the destination country shown on the order page when you generate the invoice.
- Selected currencyEUR
This reveals the currency shown in the AliExpress order interface.
- Network address context198.51.100.23 (illustrative)
The remote host receiving the HTTP request can see the network address used for the request, which can add location context.
The source issues the request without awaiting or reading a response.
The invoice button and alert fetch path
Invoice button click handler in deobfuscated content.js
js/content.jsfunction addButtons(e = 0) { const t = "_aig-print-button"; if (document.querySelector(`#${t}`)) return; const n = [".order-status > button:last-of-type", ".order-status button:last-of-type", ".order-status button.comet-btn:last-of-type", ".order-status .comet-btn:last-of-type", ".order-status-box ~ button:last-of-type"]; let r = null; for (const e of n) if (r = document.querySelector(e), r) { console.log(`[AliExpress Invoice] Found button with selector: ${e}`); break } if (!r) { const e = document.querySelectorAll(".order-status button.comet-btn"), t = ["Receipt", "Reçu", "Beleg", "Quittung", "Recibo", "Ricevuta"]; for (const n of e) { const e = n.textContent.trim().toLowerCase(); if (t.some((t => e.includes(t.toLowerCase())))) { r = n, console.log(`[AliExpress Invoice] Found "Receipt" button by text content: ${n.textContent.trim()}`); break } }!r && e.length > 0 && (r = e[e.length - 1], console.log("[AliExpress Invoice] Using last available button as fallback")) } if (!r) return e < 50 ? void setTimeout((() => { addButtons(e + 1) }), 100) : (console.error("[AliExpress Invoice] Could not find target button after", 50, "attempts"), void console.error("[AliExpress Invoice] Available buttons:", document.querySelectorAll(".order-status button"))); try { let e = r.cloneNode(!0); e.setAttribute("id", t), e.innerHTML = "Download PDF Invoice", e.addEventListener("click", storePdf), e.style.background = "#ff4747", e.style.color = "#ffffff", e.style.borderColor = "#ff4747", r.insertAdjacentElement("afterend", e), console.log("[AliExpress Invoice] Button added successfully") } catch (e) { console.error("[AliExpress Invoice] Error adding button:", e) }}Alert message construction in deobfuscated content.js
js/content.jsfunction recordMessage(e, t) { if (2 === t) console.error(e); else if (1 === t) console.warn(e); else if (0 === t) return void console.log(e); let n = document.querySelector(`#${messageContainerId}`); if (!n) { let e = document.querySelector(".order-status-box"); if (e) try { n = document.createElement("div"), n.setAttribute("id", messageContainerId), n.style.border = "1px solid #e62e04", n.style.padding = "1ex", n.style.marginTop = "1ex", n.style.display = "block", n.innerHTML = "<b>Invoice generation log</b>", e.appendChild(n), console.log("[AliExpress Invoice] Message container created on demand") } catch (e) { console.error("[AliExpress Invoice] Error creating message container:", e) } } n && (n.innerHTML += `<br/>${e}`, n.style.display = "block", chrome.runtime.sendMessage({ type: "alert", content: `%5B${getShipToFlag()}%7C${getCurrency()}%5D ${e}` }, (e => { console.log("alert:: " + e) })))}function getShipToFlag() { try { let e = document.querySelector('[class*="ship-to--info"] span:nth-of-type(1)'); if (e) { return e.textContent.trim().split("/")[1] || "Unknown" } return console.error('Element with class containing "ship-to--info" and :nth-of-type(1) not found'), "Unknown" } catch (e) { return console.error("Error in getShipToFlag:", e), "ERROR" }}function getCurrency() { try { let e = document.querySelector('[class*="ship-to"] :nth-of-type(2)'); if (e) { return e.textContent.trim() || "Unknown" } return console.error('Element with class containing "ship-to" and :nth-of-type(2) not found'), "Unknown" } catch (e) { return console.error("Error in getCurrency:", e), "ERROR" }}HTTP fetch in deobfuscated background.js
js/background.jschrome.runtime.onInstalled.addListener(onExtensionUpdated), chrome.runtime.onConnect.addListener(onConnect), chrome.runtime.onMessage.addListener((function(e, t, n) { return "alert" === e.type && fetch(`http://api.alichrome.io/alert/${e.content}`), !0}));- api.alichrome.io
Receives the /alert/ GET request containing the invoice-generation alert content, including the country and currency prefix.
What it can do
Permissions this extension asks for, as declared in version 3.6. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Store data in your browser
storage