Is Share-A-Cart – Easily Share Shopping Carts safe?
Share-A-Cart is medium risk. Creating a cart on Sam's Club or Shein makes Share-A-Cart build a GET to share-a-cart.com with site's hostname, sent at most once daily per vendor. Recorded traffic didn't exercise this; evidence is shipped code, not an observed request.…
Who publishes itShare-A-Cart - 3 other listings from the same operator, none carrying a finding
Share-A-Cart - 3 other listings 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
3 other listings published from this account, 44k+ users between them, none of them carrying a finding.
Shared hosts - 4 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
Cart creation sends vendor hostname to Share-A-Cart
Creating a cart on Sam's Club or Shein makes Share-A-Cart build a GET to share-a-cart.com with site's hostname, sent at most once daily per vendor.
Recorded traffic didn't exercise this; evidence is shipped code, not an observed request.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You create a cart on Sam's Club or Shein with the extension.
The relevant path is the create-cart message handled by the background script.
The extension prepares a Share-A-Cart request containing the shopping site's hostname.
The code rate-limits the request to once per day for each of those two vendors.
- Shopping-site hostnamehttps://www.samsclub.com
Lets Share-A-Cart know which supported shopping site you were using when the cart was created.
- Vendor bucketsamsclub
Limits the request to two vendor categories, so the request only applies when you use those supported stores.
Source-derived request shape; the recorded traffic did not trigger this vendor-specific request.
The create-cart path calls the vendor-specific redirector request
Readable redirector method
deobfuscated/js/background.jsperformRedirectorFetch(e, t) { if (["samsclub", "shein"].includes(e.id)) try { const { hostname: n } = new URL(t), a = this.lastFetchMap.get(e.id) || -1, r = Date.now(); if (-1 === a || r > a + 864e5) { this.lastFetchMap.set(e.id, r); const t = `https://share-a-cart.com/u/${encodeURIComponent(`https://${n}`)}`; fetch(t, { mode: "no-cors" }).catch(() => {}) } } catch (e) {}}Readable create-cart branch
deobfuscated/js/background.jsif ("create-cart" === n.action) { const e = d.getVendorByURL(n.url); let t; return e ? (e.reader(n.url).then(({ cartID: e, marts: n, errorCode: a }) => { a ? s({ errorCode: a }) : n ? s({ marts: n }) : t = e }).then(o.initialize).then(e => e.used()).then(() => { s({ cartID: t, vendorID: e.id, vendorDisplayName: e.displayName }) }).catch(t => { l.errorToSentry(`[vendor.reader] [${e.displayName}][url=${void 0===n?"null":n.url}]`, t, { name: e.id, tags: { type: "vendor", operation: "read", vendor: e.id } }) }), d.performRedirectorFetch(e, n.url)) : s({ errorCode: "2000" }), !0}- share-a-cart.com
Receives the encoded shopping-site hostname for the Sam's Club or Shein cart creation path.
Daily remote config controls routing settings
Share-A-Cart requests https://cdn.share-a-cart.com/config.json and stores returned routing percentage, frame-use flag, and excluded vendor list.
It fetches this on startup and daily, merges it in, and writes routing/popup values locally.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
The extension reaches its scheduled configuration check.
The alarm is created with a 1,440-minute period, and the configuration check also runs when the background service loads its settings.
The extension downloads a remote JSON configuration and stores returned settings locally.
Observed stored settings included routing chance, frame usage, and a vendor blocklist for the in-page popup.
Every 1 day
The background alarm checks for remote configuration once every 24 hours, while a shorter cache prevents immediate repeat fetches.
Observed during dynamic analysis: HTTP 200, followed by local storage writes for fetched configuration settings.
- Routing chance100
Lets the remote config set a percentage value that the extension stores for routing behavior.
- Frame usagefalse
Lets the remote config choose whether frame-based routing support is enabled or disabled in local settings.
- Popup vendor blocklistamazon, walmart, target, ebay, etsy
Lets the remote config change which supported vendors are excluded from the extension's in-page popup behavior.
These local settings show which remote-controlled values were available to the extension after the config fetch.
- Location
- chrome.storage.local fetched config settings
{ "routeChance": 100, "routeUseFrames": false, "popupInPageVendorBlocklist": [ "amazon", "walmart", "target", "ebay", "etsy" ]}Remote config fetch, daily alarm, and stored behavior controls
Readable remote config class
deobfuscated/js/background.jsconst o = "configLastFetched", s = "config";class c { constructor() { this.config = i, this.init() } static getInstance() { return c.instance || (c.instance = new c), c.instance } async init() { const { config: e } = await chrome.storage.local.get(s); e && (this.config = e) } getConfig() { return this.config } async fetchRemoteConfig() { const { configLastFetched: e } = await chrome.storage.local.get(o); if (e && Date.now() - e < 18e5) return this.config; try { const e = await fetch("https://cdn.share-a-cart.com/config.json"); if (!e.ok) throw new Error(`Failed to fetch remote config: ${e.statusText}`); const t = await e.json(); return this.config = r()(this.config, t), await chrome.storage.local.set({ [o]: Date.now(), [s]: this.config }), this.config } catch (e) { const t = e instanceof Error ? e.message : String(e); throw new Error(`Failed to fetch remote config: ${t}`) } }}Readable daily alarm and storage writes
deobfuscated/js/background.jschrome.alarms.onAlarm.addListener(({ name: e}) => { if (l.info(`Waking up for alarm: ${e}`), "fetchConfig" === e) { t.TS.getInstance().fetchRemoteConfig().then(t => { try { t.routeChance >= 0 && t.routeChance <= 100 && chrome.storage.local.set({ routeChance: t.routeChance }); const { routeUseFrames: e } = t; chrome.storage.local.set({ routeUseFrames: e }); const { popupInPageVendorBlocklist: n } = t; Array.isArray(n) && chrome.storage.local.set({ popupInPageVendorBlocklist: n }) } catch (t) { l.errorToSentry(`[alarm: ${e}] error`, t, { tags: { type: "alarm" } }) } }).catch(() => {}); try { fetch(`${t.TS.getInstance().getConfig().dbEndpoint}/aws~health-check`, { method: "GET", mode: "no-cors", cache: "no-store" }).then(async e => { e.ok && "OK" === await e.text() ? u() : u(!0) }).catch(() => { u(!0) }) } catch (e) { u(!0) } }}), chrome.alarms.create("fetchConfig", { periodInMinutes: 1440, when: Date.now()})Readable use of the stored popup blocklist
deobfuscated/js/background.jschrome.tabs.onUpdated.addListener(async (e, t, { active: n, url: a}) => { "complete" === t.status && n && a && (Hn.refresh(a), Promise.all([chrome.storage.local.get("popupInPageVendorBlocklist"), chrome.storage.sync.get("popup")]).then(([{ popupInPageVendorBlocklist: t }, { popup: n }]) => { if ("false" === n) return; if (Yn && Yn.indexOf("Firefox") >= 0) return; const r = Bn.B.getInstance().getVendorByURL(a), i = t || Xn.popupInPageVendorBlocklist; r && e && -1 === i.indexOf(r.id) && chrome.scripting.executeScript({ files: ["js/popup.js"], target: { tabId: e } }) }))})- cdn.share-a-cart.com
Serves the JSON configuration file that can update routing and popup-control settings.
Cart-Sharing Beacon Now Fires For 290 Vendors, Not Just 2
The extension sent a GET to share-a-cart.com with the site's hostname within seconds of a Sam's Club cart.
The gate flipped from an allow-list (Sam's Club/Shein) to a deny-list (Amazon/Target), so ~290 of 292 adapters can now trigger it.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You create a cart on one of the extension's 290 non-Amazon, non-Target retail vendors.
The relevant path is the same create-cart message handler documented for Sam's Club and Shein.
The extension sends that vendor's hostname to share-a-cart.com, rate-limited to once per vendor per day.
The vendor gate that used to allow only two vendors now excludes only Amazon and Target.
The vendor gate flipped from allow-list to deny-list in the latest version.
Readable allow-list version (previous shipped version)
deobfuscated/js/background.jsperformRedirectorFetch(e, t) { if (["samsclub", "shein"].includes(e.id)) try { const { hostname: n } = new URL(t), a = this.lastFetchMap.get(e.id) || -1, r = Date.now(); if (-1 === a || r > a + 864e5) { this.lastFetchMap.set(e.id, r); const t = `https://share-a-cart.com/u/${encodeURIComponent(`https://${n}`)}`; fetch(t, { mode: "no-cors" }).catch(() => {}) } } catch (e) {}}Readable deny-list version (current shipped version)
deobfuscated/js/background.jsperformRedirectorFetch(e, t) { if ((0, i.U)() !== i.u.SAC) return; if (["amazon", "target"].includes(e.id)) return; const n = r.TS.getInstance().getConfig(); try { const { hostname: a } = new URL(t), r = this.lastFetchMap.get(e.id) || -1, i = Date.now(); if (-1 === r || i > r + 864e5) { this.lastFetchMap.set(e.id, i); const t = `${n.site}/u/${encodeURIComponent(`https://${a}`)}`; fetch(t, { mode: "no-cors" }).catch(() => {}) } } catch (e) {}}- Shopping-site hostnamehttps://www.samsclub.com
Lets Share-A-Cart know which shopping site you were using when the cart was created.
- Vendor bucketsamsclub
Identifies which bundled vendor you were using; only Amazon and Target are excluded from sending this field.
Observed during dynamic analysis: the background service worker issued this request within seconds of clicking the extension's cart-creation control on a Sam's Club product page. No-cors requests are opaque, so no response status or body is available to the extension.
- share-a-cart.com
Receives the shopping-site hostname for any bundled vendor other than Amazon or Target.
+1 more finding not shown