Is EHunt - Etsy™ Rank Tool safe?
EHunt is medium risk. EHunt's Etsy review analysis collects reviews and buyer context from the page and Etsy APIs. Background posts the array (text, name/URL, transaction ID, rating, images) to esale.io. Not triggered in dynamic analysis; no body captured.
Who publishes itTouhouProjInc - 2 other listings from the same operator, 2 of them carrying a finding
TouhouProjInc - 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.
Same store account
2 other listings published from this account, 70k+ users between them. 2 of them carry a finding.
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
Etsy review details sent to esale.io for AI analysis
EHunt's Etsy review analysis collects reviews and buyer context from the page and Etsy APIs.
Background posts the array (text, name/URL, transaction ID, rating, images) to esale.io.
Not triggered in dynamic analysis; no body captured.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You start EHunt's review analysis feature on an Etsy listing or shop page.
The code path checks that enough reviews are available before asking for analysis.
The extension gathers Etsy review records and sends the review array to esale.io.
The request is tied to the current shop or listing and includes the parsed reviews count.
- Review textGreat quality and shipped fast (illustrative)
Shows what a buyer wrote about a product or shop and is sent with the analysis request.
- Transaction ID3289107541 (illustrative)
Links a review record to a specific Etsy transaction when Etsy exposes that value on the page or API response.
- Buyer nameAvery Stone (illustrative)
Can identify the Etsy buyer associated with the review when a name is present.
- Buyer profile URLhttps://www.etsy.com/people/averyhandmade (illustrative)
Connects the review to a specific Etsy profile page when the profile link is present.
- Rating, image, and date5 stars, 2026-06-18, https://i.etsystatic.com/12345678/r/il/example.jpg (illustrative)
Adds context about the review contents and timing that travels with the review record.
No POST body was captured because the review-analysis path was not triggered during dynamic analysis.
- Content-Type
- application/json
Review parsing and the esale.io analysis request
Readable review parsers and Etsy review fetchers
js/content.jsRl = e => { const t = []; return e.forEach(e => { const a = e.querySelector("p.prose")?.textContent.trim() || ""; let l = ""; const s = e.querySelector("[data-transaction-id]"); l = s ? s.dataset.transactionId : ""; const o = e.querySelector(".shop2-review-attribution"); let n = "", i = ""; if (o) { const e = o.querySelector("a"); e && (n = e.textContent.trim(), i = e.href) } const r = e.querySelector('input[name="rating"]'), c = r ? parseInt(r.value) : 0, u = ""; let d = "", p = ""; o && (d = o.textContent.replace(n, "").trim(), d && (p = Math.floor(Date.parse(d) / 1e3))); const _ = i && i.match(/\/people\/([^/?]+)/)?.[1] || ""; t.push({ content: a, transaction_id: l, auther: n, auther_url: i, rateing: c, images: u, date: p, auther_name: _ }) }), t}, Sl = e => { const t = []; return e.forEach(e => { const a = e.reviewContent?.reviewText || "", l = e.transactionId, s = e.buyerInfo?.name || "", o = e.buyerInfo?.profileUrl || "", n = e.reviewInfo?.rating || 0, i = e.reviewContent?.appreciationPhotoUrl || "", r = e.reviewInfo?.reviewDate || "", c = r ? Math.floor(Date.parse(r) / 1e3) : "", u = e.buyerInfo?.profileUrl && e.buyerInfo.profileUrl.match(/\/people\/([^/?]+)/)?.[1] || ""; t.push({ content: a, transaction_id: l, auther: s, auther_url: o, rateing: n, images: i, date: c, auther_name: u }) }), t}, Fl = async (e, t, a = 1) => { const l = { log_performance_metrics: !0, specs: { deep_dive_reviews: ["Etsy\\Modules\\ListingPage\\Reviews\\DeepDive\\AsyncApiSpec", { listing_id: parseInt(e), shop_id: parseInt(t), scope: "listingReviews", page: a, sort_option: "Relevancy", rating_filter: null, tag_filters: [], should_lazy_load_images: !1, should_show_variations: !0 }] } }, s = await fetch("https://www.etsy.com/api/v3/ajax/bespoke/member/neu/specs/deep_dive_reviews", { method: "POST", mode: "cors", headers: { "Content-Type": "application/json", "x-csrf-token": Il(), accept: "*/*", origin: "https://www.etsy.com" }, referrerPolicy: "strict-origin-when-cross-origin", body: JSON.stringify(l) }); return await s.json()}, Pl = async (e, t = 1) => { const a = new URLSearchParams({ log_performance_metrics: !1, "specs[shop-reviews][]": "Shop2_ApiSpecs_Reviews", "specs[shop-reviews][1][shopname]": e, "specs[shop-reviews][1][page]": t, "specs[shop-reviews][1][reviews_per_page]": 10, "specs[shop-reviews][1][should_hide_reviews]": !0, "specs[shop-reviews][1][is_in_shop_home]": !0, "specs[shop-reviews][1][sort_option]": "Relevancy", runtime_analysis: !1 }), l = await fetch(`https://www.etsy.com/api/v3/ajax/bespoke/public/neu/specs/shop-reviews?${a.toString()}`, { method: "GET", mode: "cors", headers: { accept: "*/*", "x-detected-locale": "VND|en-US|VN", "x-etsy-protection": "1" }, referrerPolicy: "strict-origin-when-cross-origin" }); return await l.json()}Readable extension message carrying reviews
js/content.jsZ = async (e, t, a, l) => new Promise(s => { chrome.runtime.sendMessage({ option: "review_analysis_ask", shop_id: e, listing_id: t, reviews_num: a, reviews: l, is_shop: "shop_detail" == D.value }, e => s(e))})Readable background handler posting to esale.io
js/background.jsreview_analysis_ask: () => { chrome.storage.local.get("token", e => { const t = { "Content-Type": "application/json" }; e.token && (t["AUTHORIZATION"] = e.token), fetch(`${o}/api/reviews/e-analyse?${s}`, { method: "POST", body: JSON.stringify({ shop_id: a.shop_id, listing_id: a.listing_id, reviews_num: a.reviews_num, type: 1, is_shop: a.is_shop || 0, reviews: a.reviews, version: +s.replaceAll(".", "") }), headers: t }).then(e => e.json()).then(function(e) { return c(e), !0 }).catch(() => (_(), c(), !0)) })}- www.etsy.com
Source of listing and shop review data that the content script fetches and parses.
- esale.io
Receives the assembled review-analysis request from the background script.
What it can do
Permissions this extension asks for, as declared in version 2.3.6. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 2.3.9, which we have not unpacked yet.
Read and change your data on etsyhunt.com
*://*.etsyhunt.com/*
Schedule its own background tasks
alarms
See the address and title of every tab you have open
tabs
Read and change cookies, including the ones that keep you signed in
cookies
Store data in your browser
storage