Is PoshPop | Poshmark Closet Bot safe?
PoshPop | Poshmark Closet Bot can forward Poshmark cookies and request headers to poshpoptools.com in error logs.
When some Poshmark API requests fail, the extension's error logging path can include request headers, including Poshmark cookies, in a message sent to its remote iframe at poshpoptools.com. It also sets a Chrome uninstall feedback URL that includes the authenticated user's email address when Chrome opens the Tally form after uninstall.
Who publishes ithttps://poshpopassistant.com/ - no other listings under this identity
https://poshpopassistant.com/ - no other listings under this identity
What this publisher told the store about itself, and the other listings that told it the same thing.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Cookie headers can reach PoshPop iframe logs
Starting a Poshmark sharing action makes the extension create API requests with page cookies in the headers.
A failed 400-series request has its headers copied into a log, forwarded to the PoshPop iframe at https://poshpoptools.com/.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
You start a Poshmark sharing action from the PoshPop sidebar.
If a handled Poshmark request failure occurs, the extension can copy request headers, including cookies, into a log sent to the PoshPop iframe.
- Your Poshmark cookiescookie: ui=us; jwt2=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 (illustrative)
These values can identify your signed-in Poshmark session when they are copied from the page into a request header.
- The Poshmark API URLURL: https://poshmark.com/vm-rest/users/self/shared_posts/123456789 (illustrative)
This shows which Poshmark action failed and can reveal the item or sharing workflow tied to your account activity.
- Request method and headersMethod: PUT; Headers: accept, content-type, cookie (illustrative)
The log records the request method and every header name and value that the failed request carried.
- Log message typetype: EXTENSION_LOG; level: error
This identifies the message as an extension log sent to the remote iframe rather than a local-only browser console entry.
The content script loads this host as the PoshPop iframe; the cookie-bearing log is then delivered to that iframe with postMessage. No HTTP request body was recorded for this claim.
The request, logging, and iframe paths in the shipped extension
Source map: Share.editShareAPICall and Share.normalShareAPICall
src/classes/Share.ts async editShareAPICall(currentItem: ProductReturnedInfo): Promise<void> { log("Sharing API Call") const request = new Request(productDetailsURL + currentItem?.id, { method: "POST", headers: { Accept: "application/json", "content-type": "application/json", cookie: document.cookie }, body: JSON.stringify(currentItem) }) const response = await fetch(request) if (!response.ok) { if ((response.status >= 500 && response.status < 600) || response.status === 403) throw new NotReportableException("Failed to connect to Poshmark") throw new RequestException( `Error: ${response.statusText} status: ${response.status}`, request, response ) } const jsonRes = await response.json() //poshmark returned error if (jsonRes.error) { this.poshmarkErrorHandler(jsonRes) } } /** * Description placeholder * * @async * @param {ProductReturnedInfo} currentItem * @returns {Promise<void>} */ async normalShareAPICall(currentItem: ProductReturnedInfo): Promise<void> { const request = new Request(this.sharingApi + currentItem?.id, { method: "PUT", headers: { Accept: "application/json", "content-type": "application/json", cookie: document.cookie } }) const response = await fetch(request) if (!response.ok) { if ((response.status >= 500 && response.status < 600) || response.status === 403) throw new NotReportableException("Failed to connect to Poshmark") throw new RequestException( `Error: ${response.statusText} status: ${response.status}`, request, response ) } const jsonRes = await response.json() //poshmark returned error if (jsonRes.error) { this.poshmarkErrorHandler(jsonRes) } }Source map: sendLogToWebsite and ExtensionLogger
src/utilities/logExtension.tsfunction sendLogToWebsite(logDetails: LogMessage): void { try { const poshmarkStore = userPoshmarkUserStore() const payload = { type: "EXTENSION_LOG", log: { //TODO: Context invalidation , store version beforehand in global index like the other data version: EXTENSION_VERSION, country: getCountry(), ...logDetails, context: logDetails.context, store: poshmarkStore.username.value }, timestamp: new Date().toISOString() } sendWindowMessage(payload) } catch (e) { console.error("Failed to send log to website:", e) }}/** * Wrapper for logging extension events. */export const ExtensionLogger = { info: (message: string, context?: any) => sendLogToWebsite({ level: "info", message, context }), warn: (message: string, context?: any) => sendLogToWebsite({ level: "warn", message, context }), error: (message: string, context?: any) => sendLogToWebsite({ level: "error", message, context }), debug: (message: string, context?: any) => sendLogToWebsite({ level: "debug", message, context })}Source map: convertArgsToTruncatedText
src/utilities/index.tsfunction convertArgsToTruncatedText(args: any[]): string { const lines: string[] = [] for (const arg of args) { if (typeof arg === "string") { lines.push(arg) } else if (arg instanceof Error) { if ( arg instanceof NotReportableException || (arg instanceof UserActivityException && !arg.isReportable) ) continue // Use stack trace for Errors lines.push(arg.stack || arg.toString()) if (arg instanceof RequestException) { const { request, response, requestPayload } = arg // Log Request Metadata lines.push("--- Request Details ---") lines.push(`URL: ${request.url}`) lines.push(`Method: ${request.method}`) // Log Request Headers lines.push("Headers:") request.headers.forEach((value, name) => { lines.push(` ${name}: ${value}`) }) if (requestPayload) { lines.push("\nRequest Payload (Body):") lines.push(JSON.stringify(requestPayload, null, 2)) } lines.push("--- Response Details ---") lines.push(`Status: ${response.status} ${response.statusText}`) lines.push(`Content-Type: ${response.headers.get("content-type") || "N/A"}`) // Log Response Headers lines.push("Headers:") response.headers.forEach((value, name) => { lines.push(` ${name}: ${value}`) }) } } else { // Use JSON.stringify with the replacer for everything else try { // The replacer handles truncation before stringification lines.push(JSON.stringify(arg, truncatingReplacer, 2)) } catch (e) { // Fallback for objects with circular references lines.push(`[Object failed to stringify: ${String(e)}]`) } } } return lines.join("\n")}Source map: sendWindowMessage
src/utilities/index.tsexport const sendWindowMessage = (data: any) => { const iframe = document?.getElementById(POSHPOP_SIDEBAR_ID) as HTMLIFrameElement iframe?.contentWindow?.postMessage(data, "*")}Source map: iframe creation and remote URL constant
src/view/PoshPopApp.vue and src/constants/environment.tsconst initAndLoadIframe = async () => { if (isInitalized.value) return //Embedding iframe dynamically to load website in previously opened tab. Doesn't load it otherwise const iframe = document.createElement("iframe") iframe.setAttribute("src", remoteServerURL + '?timestamp=' + new Date().getTime() + "&version=" + manifestData.version + "&origin=" + window.location.origin) iframe.setAttribute("name", String(Date.now())) iframe.setAttribute("id", POSHPOP_SIDEBAR_ID) iframe.style.position = "relative" iframe.style.zIndex = '2' iframe.setAttribute("allow", "clipboard-read; clipboard-write") iframe.onload = initializePoshpopAndMessageListeners sidePanelRef.value.append(iframe) isInitalized.value = true;}export const remoteServerURL = process.env.VUE_APP_WEB_UI_SERVER_URL ?? ""export const serverUrl = process.env.VUE_APP_SERVER_URL ?? ""- poshpoptools.com
Hosts the PoshPop iframe that receives EXTENSION_LOG messages via postMessage.
- poshmark.com
Receives the original sharing API request whose headers are copied into the error log after certain failures.
Where it sends data
Destinations our analysis observed PoshPop | Poshmark Closet Bot contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.
- poshpoptools.com
PoshPop | Poshmark Closet Bot sends data to poshpoptools.com. No other extension we have analysed sends data here.
- tally.so
PoshPop | Poshmark Closet Bot sends data to tally.so. No other extension we have analysed sends data here.