Is FBA calculator for Amazon Sellers : SellerApp safe?
fba calculator is medium risk. The extension opens a WebSocket to SellerApp's scrapernet service with query fields for country, extension type, IP, ZIP, and Amazon login state. The source shows it can also receive scraping jobs and fetch pages with browser credentials.
Who publishes itSellerApp - 4 other listings from the same operator, 4 of them carrying a finding
SellerApp - 4 other listings from the same operator, 4 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
4 other listings published from this account, 66k+ users between them. 4 of them carry a finding.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
SellerApp WebSocket sends Amazon session context
The extension opens a WebSocket to SellerApp's scrapernet service with query fields for country, extension type, IP, ZIP, and Amazon login state.
The source shows it can also receive scraping jobs and fetch pages with browser credentials.
The extension background script starts after installation or browser startup.
It opens a persistent connection to SellerApp and attaches browser and Amazon-session context to the connection URL.
| Field | Value | Why it matters | |
|---|---|---|---|
Country | geo=GB | This tells the remote service which marketplace or geography your browser is being associated with. | |
Network address field | ip_address=undefined | When populated, this can identify the network your browser is using; in the captured run the field was sent with an undefined value. | |
ZIP-code field | zip_code=undefined | Can reveal a more specific delivery/shopping region when Amazon returns one; in the captured run it was sent as undefined. | |
Amazon login state | is_user_logged_in=true | This tells the remote service whether the browser appeared to be signed in to Amazon during the check. | |
Extension mode | ext_type=fba-calc | This identifies the extension workflow using the socket. |
Socket startup, health frames, and server-sent Amazon scraping jobs
t.initializeSocket = (e, t) => {
(0, o.get)("socket_id").then((e => {
T = e
})).catch((() => {
T = (0, a.v4)(), (0, o.set)("socket_id", T).then((() => {}))
})).finally((() => r(void 0, void 0, void 0, (function*() {
var n;
try {
E = yield(0, o.get)("country_id"), g = yield(0, o.get)("ip_address")
} catch (e) {
const t = yield fetch("https://lumtest.com/myip.json"), n = yield t.json();
E = n.country, g = n.ip, (0, o.set)("country_id", E), (0, o.set)("ip_address", g)
}
let r = "";
try {
r = yield(0, s.fetchDetails)(h[E])
} catch (e) {}
if (r) {
y = (0, s.parse)(r, i.zipCodeSchema);
const e = (0, s.parse)(r, i.userLoggedInSchema);
b = !(null === (n = null == e ? void 0 : e.sign_in_button_url) || void 0 === n ? void 0 : n.includes("signin"))
} else y = {
zip_code: ""
};
f = (0, a.v4)(), p = new WebSocket(`wss://scrapernet.sellerapp.com/scraper-net/websocket?id=${T}&instance_id=${f}&geo=${E}&ext_type=${e}&account_id=${t}&ip_address=${g}&zip_code=${null==y?void 0:y.zip_code}&is_user_logged_in=${b}`), w(p, T, e)
}))))
}; const w = (e, n, i) => {
e.onopen = () => r(void 0, void 0, void 0, (function*() {
console.log("WebSocket connection established........"), D = 1e3, M = 6e4, L = 100, A && clearInterval(A), A = setInterval((() => {
e.send(JSON.stringify({
socket_id: n,
status: 200,
type: "health",
zip_code: null == y ? void 0 : y.zip_code
}))
}), 3e4);
try {
const t = yield Y();
e.send(JSON.stringify({
socket_id: n,
status: 200,
type: "login_status",
login_status: t
}))
} catch (e) {
console.log("Error in checking multiple countries login")
}
})), e.onmessage = function(i) {
return r(this, void 0, void 0, (function*() {
const o = i.data,
u = JSON.parse(o);
if (u.url || "RUFUS_EXTENDED" === u.parser_config_url.page_type) {
let i = u.url;
const o = u.parser_config_url.page_type;
if ("RUFUS_EXTENDED" === o) {
const e = h[u.parser_config_url.geo] || "https://www.amazon.in";
if (i = `${e}/rufus/cl/streaming?tabId=${u.tabId||(0,a.v4)()}&ref=nl_cl_dsk_csq&programId=NILE_CLASSIC:desktop-cl`, !u.rufusToken) {
const n = u.url || e,
r = yield(0, s.fetchDetails)(n);
"string" == typeof r && (u.rufusToken = yield(0, t.getRufusToken)(r, n))
}
}
if (u.zipcode && i) {
const e = /https?:\/\/(www\.)?([a-zA-Z0-9.-]+)/,
t = c.default.parseUrl(i).url.match(e);
if (t) {
const e = t[0],
n = yield(0, s.fetchDetails)(e);
yield(0, l.setZipCode)(n, e, u.zipcode)
}
}
const d = "hHqVzDHmU7Is8nzCo2Y2worrxE6Le8i4eNuimVauLTHwAAAAAGmv31piMDI3OTI3Mi0xN2VjLTRkM2YtYjI5Yi0yMjlkMjViMmJjOGM=",
m = "RUFUS_EXTENDED" === o ? {
method: "POST",
body: {
queryContext: {
query: "best mobile cover to buy",
actionType: "SEARCH",
qis: "NileCLTextInput"
},
pageContext: {
pageType: "SEARCH_RESULTS",
targetPageType: "SEARCH_RESULTS",
originPageType: "SEARCH_RESULTS",
targetUrl: "https://www.amazon.in/s?k=shoes&crid=K5MCO7Y02P4D&sprefix=shoes%2Caps%2C396&ref=nb_sb_noss_2",
originUrl: "https://www.amazon.in/s?k=shoes&crid=K5MCO7Y02P4D&sprefix=shoes%2Caps%2C396&ref=nb_sb_noss_2",
targetPageMetadata: [{
type: "KEYWORDS",
value: "shoes"
}],
pageMetadata: [{
type: "KEYWORDS",
value: "shoes"
}],
originPageMetadata: [{
type: "KEYWORDS",
value: "shoes"
}]
}
},
headers: {
"Content-Type": "application/json",
"anti-csrftoken-a2z": d,
Accept: "*/*",
"Cache-Control": "no-cache",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin"
},
isStream: !0
} : {};
(0, s.fetchDetails)(i, !1, 1, 500, m).then((a => r(this, void 0, void 0, (function*() {
let r = {};
"object" == typeof a && 200 !== a.status ? r = {
socket_id: n,
data: {},
timestamp: new Date,
status: a.status,
scrapeUrl: i,
instance_id: f,
version: _.version,
html: "",
request_id: u.request_id
} : "string" == typeof a && ("RUFUS_EXTENDED" === o ? r = yield(0, t.getRufusExtendedDetails)(a, u.url, u.request_id): "KEYWORD_SEARCH" === o ? r = yield I(a, i, u.parse_html, u.all_pages || !1, (null == u ? void 0 : u.page_limit) || 7, u.request_id): "PRODUCT_LISTING" === o ? r = C(a, i, u.parse_html, u.request_id) : "SELLER_PROFILE" === o ? r = k(a, i, u.parse_html, u.request_id) : "QNA_DETAILS" === o ? r = O(a, i, u.parse_html, u.request_id) : "REVIEW_DETAILS" === o ? r = yield v(a, i, u.parse_html, u.request_id): "BEST_SELLER" === o ? r = R(a, i, u.parse_html, u.request_id) : "OFFERS_DETAILS" === o ? r = yield P(a, i, u.parse_html, u.request_id): "RUFUS_DETAILS" === o && (r = yield(0, t.getRufusDetails)(a, u.url, u.query, u.request_id, u.searchSource))), e.send(JSON.stringify(r))
})))).catch((t => {
e.send(JSON.stringify({
error: "parse error",
request_id: u.request_id,
status: 500
}))
}))
}
}))
}, e.onerror = function(e) {
A && clearInterval(A), console.error("WebSocket error:", e)
}, e.onclose = function(n) {
A && clearInterval(A),
function(n) {
e && e.readyState !== WebSocket.CLOSED || (D < M && (D *= 2), L > 0 ? (console.log(`Attempting to reconnect in ${D} ms. Attempts left: ${L}`), setTimeout((function() {
(0, t.initializeSocket)(n, S), L--
}), D)) : console.error("Maximum reconnection attempts reached. Unable to establish WebSocket connection."))
}(i)
}
}, t.fetchDetails = (e, t = !1, n = 1, a = 500, s = {}) => r(void 0, void 0, void 0, (function*() {
var r, i;
let o = 0;
for (; o <= n;) try {
const c = yield fetch(e, {
method: s.method || "GET",
mode: "cors",
credentials: "include",
headers: s.headers || {},
body: s.body ? JSON.stringify(s.body) : void 0
});
if (c.url.includes("ap/signin") && (o = n), !c.redirected || !t || o === n) {
if (o === n && 200 !== c.status) return {
status: 302
};
if (200 === c.status) {
if (s.isStream) {
if (!c.body) throw new Error("ReadableStream not supported in response");
const e = c.body.getReader(),
t = new TextDecoder("utf-8");
let n = "",
a = "";
for (;;) {
const {
value: s,
done: i
} = yield e.read();
if (i) break;
a += t.decode(s, {
stream: !0
});
const o = a.split("\n");
a = o.pop() || "";
for (const e of o) {
if (!e.startsWith("data:")) continue;
const t = e.replace(/^data:\s*/, "").trim();
if (t) try {
n += null !== (r = JSON.parse(t).htmlRenderedChunk) && void 0 !== r ? r : ""
} catch (e) {
console.error("❌ Invalid JSON chunk:", e.message)
}
}
}
if (a.startsWith("data:")) {
const e = a.replace(/^data:\s*/, "").trim();
if (e) try {
n += null !== (i = JSON.parse(e).htmlRenderedChunk) && void 0 !== i ? i : ""
} catch (e) {
console.error("❌ Invalid JSON chunk:", e.message)
}
}
return n
}
return yield c.text()
}
return {
status: c.status
}
}
o++, yield new Promise((e => setTimeout(e, a * Math.pow(2, o))))
} catch (e) {
return {
status: 500
}
} })), (0, zo.initializeSocket)("fba-calc", ""), chrome.runtime.onUpdateAvailable.addListener((function(e) {
console.log(e.version), chrome.runtime.reload()
}))- scrapernet.sellerapp.com
SellerApp scrapernet WebSocket endpoint receiving connection metadata, health frames, login-status results, and parsed scraping results.
- lumtest.com
IP geolocation lookup used by the extension when country and IP address are not already stored locally.
- amazon.com
Amazon marketplace pages are fetched by the extension when the socket job path requests Amazon page data.