Is フリマアシスト safe?
フリマアシスト reads the user's Mercari auth token from localStorage and routes authenticated Mercari API calls through the developer's own Google Cloud Function.
The extension's content script extracts the Mercari login token stored in the browser and attaches it to requests, which are AES-encrypted with a hardcoded key and forwarded to asia-northeast1-furima-assist-free.cloudfunctions.net. This cloud proxy then makes authenticated calls to api.mercari.jp on the user's behalf. Separately, the extension fetches a remote configuration from kiyomaro.net every minute to control ad banners injected into flea market pages.
Who publishes itきよまろ - 1 other listing from the same operator, none carrying a finding
きよまろ - 1 other listing 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
1 other listing published from this account, 2k+ users between them, none of them carrying a finding.
Shared hosts - 9 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
Ad Banner Content Fetched from Developer's Remote Server
On flea market sites, the extension polls kiyomaro.net every minute by default for config data controlling injected ad banners and links.
We captured 53 such requests; a server change alters injections without a Store update.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
You open a supported flea market page such as jp.mercari.com.
The content script and background service worker activate for the matched domain.
The extension fetches kiyomaro.net/bt/button.json and caches the returned configuration, which determines what banner images and links are injected into the page.
If the remote config is unavailable, the last cached version stored in chrome.storage.local is used.
Returns a JSON object with a furimaAssist.free field containing banner image URLs, link destinations, and optional text. Observed 38 HTTP 200 and 15 HTTP 429 responses across 53 requests during dynamic analysis.
Every 1 minute
The background worker re-fetches the remote config at most once per minute (default). The poll interval itself is configurable via the fetchIntervalMinutes field returned by the same remote endpoint.
Remote config fetch and storage in background service worker
const o = async () => { const e = "https://kiyomaro.net/bt/button.json?t=" + (new Date).getTime(); try { const r = await fetch(e); return await r.json() } catch (e) { console.error("error", e) } return {}},c = async e => { await Object(n.g)({ af: JSON.stringify(e) }) // write to chrome.storage.local},i = async () => { var e, r; const t = 60 * ((null === (e = properties) || void 0 === e || null === (r = e.af) || void 0 === r ? void 0 : r.fetchIntervalMinutes) || 1) * 1e3 // poll interval controlled by the remote config itself- kiyomaro.net
Developer-operated server. Returns the furimaAssist.free JSON object controlling banner image URLs, link destinations, poll interval, and related ad display settings.
- furima-assist.com
Developer-operated server. Hosts the default banner image and link assets referenced in property.js; images are fetched via background relay before insertion.
Mercari API Calls Proxied Through Developer's Cloud Function
On Mercari, the extension encrypts requests (URL, method, body, timestamp) with an embedded key and hands them to a developer-run Google Cloud Function, which decrypts and makes the call.
Anyone extracting the key can decrypt this traffic.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-319
- Source
- Dynamic sandbox
You use an extension feature that reads Mercari item data or user information.
The extension's class ve (an API client) is instantiated when no local auth token is available in localStorage.
The extension encrypts Mercari API requests with a key hardcoded in the bundle and routes them through a developer-run Google Cloud Function instead of Mercari directly.
The AES ciphertext is placed in the x-parameters header of a POST to asia-northeast1-furima-assist-free.cloudfunctions.net/request.
AES encryption with hardcoded passphrase (content.js:50604-50628)
class ve { async request(e = {}) { e.ts = (new Date).getTime(); const t = { headers: { "x-parameters": be.AES.encrypt( JSON.stringify(e), "_YOS+$'+U~iufw=$>t04HIhi(lB\"7x$v" // hardcoded AES passphrase ).toString() } }; let r = {}; try { const e = { options: t, type: "API" }; r = await a.a.runtime.sendMessage(e); // relay to background } catch (e) { console.log({ error: e }) } return console.debug({ params: e, response: r }), r }}Service worker forwards the encrypted payload to the cloud function (background.js:1610-1621)
const a = "https://asia-northeast1-furima-assist-free.cloudfunctions.net/request", i = async e => { console.log({ on: "apiRequest", endpoint: a, options: e }); const r = await fetch(a, { ...e, method: "post" }); return await r.text() }Demonstrates that the hardcoded AES passphrase in the extension bundle can decrypt any x-parameters header value captured from network traffic or server-side logs.
- Node.js 16+
- npm install crypto-js
// Requires: npm install crypto-js// Usage: node decrypt-x-parameters.js '<base64-ciphertext>'// The ciphertext is the value of the x-parameters header from any proxied request.const CryptoJS = require('crypto-js');const HARDCODED_KEY = '_YOS+$\'+U~iufw=$>t04HIhi(lB"7x$v';const ciphertext = process.argv[2];if (!ciphertext) { // Example: encrypt a sample payload to show round-trip const sample = { url: 'https://api.mercari.jp/items/get?id=m12345678&_item_photo_format=detail', method: 'GET', ts: Date.now() }; const encrypted = CryptoJS.AES.encrypt(JSON.stringify(sample), HARDCODED_KEY).toString(); console.log('Sample encrypted x-parameters header value:'); console.log(encrypted); console.log('\nDecrypted back:'); const decrypted = CryptoJS.AES.decrypt(encrypted, HARDCODED_KEY).toString(CryptoJS.enc.Utf8); console.log(JSON.parse(decrypted));} else { const decrypted = CryptoJS.AES.decrypt(ciphertext, HARDCODED_KEY).toString(CryptoJS.enc.Utf8); console.log('Decrypted payload:'); console.log(JSON.parse(decrypted));}- 1Install dependency: npm install crypto-js.
- 2Run without arguments to see a sample round-trip: node decrypt-x-parameters.js.
- 3Pass a captured x-parameters header value to decrypt it: node decrypt-x-parameters.js '<base64-ciphertext>'.
- asia-northeast1-furima-assist-free.cloudfunctions.net
Developer-run Google Cloud Function (project furima-assist-free). Receives proxied Mercari API requests, decrypts the x-parameters header, executes the call.
Where it sends data
Destinations our analysis observed フリマアシスト 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.
- api.mercari.jp
フリマアシスト sends data to api.mercari.jp. No other extension we have analysed sends data here.
- asia-northeast1-furima-assist-free.cloudfunctions.net
フリマアシスト sends data to asia-northeast1-furima-assist-free.cloudfunctions.net. No other extension we have analysed sends data here.
- furima-assist.com
フリマアシスト sends data to furima-assist.com. No other extension we have analysed sends data here.
- kiyomaro.net
フリマアシスト sends data to kiyomaro.net. No other extension we have analysed sends data here.