Is 買い物ポケット safe?

High risk

買い物ポケット 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.…

GMOインサイト株式会社v3.73.1Chrome Web Store
78Risk
Who publishes it

GMOインサイト株式会社 - 2 other listings 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.

Publisher
GMOインサイト株式会社
Declared legal entity
GMOインサイト株式会社

Same store account

2 other listings published from this account, 401k+ users between them, none of them carrying a finding.

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

HighunwantedCWE-359Dynamic sandbox

When server config enables the mljs module, this extension gathers page title, URL, referrer, browser/screen details, and a persistent ID from visited pages.

What actually happens

You did this

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 did this

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.

What's stored on your device

Server-provided config enabled this browsing-data module, and its local timer had already cleared during the confirmed run.

Location
chrome.storage.local values set after api.kaipoke.jp/bar/init
Contents
{  "modules": "{\"aijs\":false,\"mljs\":true,\"restricted\":false}",  "mljs-date": "1776208021139"}

Plaintext fields assembled before encryption

  • 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.

Captured request

GEThttps://lfs3.gmo-insight.jp/

Dynamic analysis observed six GET requests during Amazon.co.jp navigation; each had i, k, d, and c=1 query parameters and no request body.

Headers
Content-Type
application/x-www-form-urlencoded

When this fires

Every 2 days

After the local mljs timer is set, the sender waits about 48 hours before allowing another send.

The code that does this

Content script collection and service worker sender

Readable version

Readable content-script flow

contents.js
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);  }};

Readable service-worker flow with decoded constants

background.js
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;  }});

External destination receiving the browsing report

    • 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

MediumunwantedCWE-359Dynamic sandbox

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.

What actually happens

You did this

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 did this

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.

Captured request

POSThttps://log.estart.jp/js/v3/event/kaipoke/ec_log

200 OK from TreasureData ingestion endpoint (log.estart.jp is the public TD JS-SDK ingest host, table=ec_log).

Headers
Accept
application/json
Content-Type
application/json
X-TD-Write-Key
<TD write key fetched from api.kaipoke.jp/bar/init>
Body
{  "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}

What gets sent to log.estart.jp on every shopping event

  • 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 code that does this

The shipped service-worker code that fires the events, alongside a readable rewrite.

Readable version

trackEcLog assembler

// 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);}

Amazon product-page handler

// 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 {}}

Add-to-cart / Buy-now / 1-click handler

// 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 {}}

TreasureData transport

// 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) }  );}

What's stored on your device

Generated once at install and stored permanently. Sent as panelist_id/uuid on every event, linking all your activity to one profile.

Location
chrome.storage.local key `kaipoke_uuid`
Contents
"58294cfa-9b9d-415c-b475-0ec026500124"

Where the shopping events end up

    • 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.

Reproduce it yourself

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.

Requires
  • Chrome with Developer mode enabled
  • kaimono-pocket extension installed and initialized (visit one Amazon.co.jp page first so /bar/init has run)
kaimono-pocket-trace.js · js
// 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.');})();
How to run it
  1. 1Install kaimono-pocket (pgmbeccjfkdbpdjfoldaahpfamjjafma).
  2. 2Open its service worker DevTools.
  3. 3Paste this script.
  4. 4Visit an Amazon.co.jp product page.
  5. 5Watch for POSTs to log.estart.jp with price, URL, panelist_id.

Server-controlled tracking module harvests per-page browsing data

MediumunwantedCWE-829Dynamic sandbox

Every page you visit triggers an encrypted report to lfs3.gmo-insight.jp.

What actually happens

You did this

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 did this

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.

Captured request

POSThttps://api.kaipoke.jp/bar/init

Returns JSON including modules.mljs=true and treasureData credentials (database name + write key) for the secondary TreasureData tracking pipeline at log.estart.jp.

Headers
Content-Type
multipart/form-data
Body
model={"dv":"chrome","sitecode":"404"}

Fields collected by the mljs tracking module on each page visit

  • 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.

Why you can't catch this in DevTools

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.

Decoded value
https://lfs3.gmo-insight.jp

The code that does this

Remote-flag check and executeMljs activation in background.js

Readable version
// 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}

External endpoints reached by this tracking module

    • 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.

What it can do

Permissions this extension asks for, as declared in version 3.73.0. 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 3.73.1, which we have not unpacked yet.

  • Read and change your data on every site you visit

    http://*/*

  • Read and change your data on every secure site you visit

    https://*/*

  • Add items to the right-click menu

    contextMenus

  • Store data in your browser

    storage

  • See the address and title of every tab you have open

    tabs

  • See every page you navigate to, as you navigate to it

    webNavigation

  • Watch every request your browser makes

    webRequest

  • Run its own code inside the pages you visit

    scripting

Updated 22 October 2026pgmbeccjfkdbpdjfoldaahpfamjjafma