Is AutoTDK - SEO Search Keyword Tool safe?

Medium risk

AutoTDK is medium risk. Confirming the AI detector in AutoTDK collects visible page text and sends up to 2,000 characters plus the page host to autotdk.com. The content script requires confirmation and a minimum length; captured traffic showed only a favicon hit.

TouhouProjIncv1.3.0Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

AI Detector Sends Page Text to AutoTDK

Confirming the AI detector in AutoTDK collects visible page text and sends up to 2,000 characters plus the page host to autotdk.com.

The content script requires confirmation and a minimum length; captured traffic showed only a favicon hit.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You confirm the AI detector feature on a page.

The UI wires the confirmation component's `onSure` handler to the detector function.

The extension did this

The extension extracts page text and sends it to AutoTDK for analysis.

The content script sends up to 2,000 characters and the current host to the background service worker.

02EvidenceFIELD TABLE
Fields prepared for the detector request
FieldValueWhy it matters
Page text
2026 SEO checklist: improve page titles, headings, internal links, and content depth. (illustrative)Text from the page you are checking leaves the browser for analysis; private or internal page content can be included.
Page host
www.example.com (illustrative)The receiving service can associate the submitted text with the site you were viewing.
Extension source label
seo_search_keyword_toolThe request marks the submission as coming from the SEO Search Keyword Tool extension.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://autotdk.com/api/text/detect-ai?f=seo_search_keyword_tool&h=www.example.com
The recorded traffic for this claim did not include this POST; earlier traffic showed only GET https://autotdk.com/favicon.ico.
Headers
sourceseo_search_keyword_tool
Content-Typeapplication/json
04EvidenceCODE COMPARE
The code that does this

The detector code path that prepares and sends page text

What it actually does
Readable content script extracts visible text and sends content plus hostcontent/content.js
detect: async () => {
  l.value = !0, i.globalLoading = !0, d.needSure = !1, h.timers = 6;
  try {
    if (!document.querySelector("h1")) throw d.rules_tip = t("ai-detector.no_h1_tips").replace("[check]", ""), l.value = !1, i.globalLoading = !1, new Error("no h1");
    const e = function(e = 0) {
        try {
          const t = document.createElement("div");
          let a = document.querySelector("h1");
          if (a) {
            const e = window.getComputedStyle(a);
            "none" !== e.display && "hidden" !== e.visibility || (a = null)
          }
          if (a) {
            const e = document.createRange();
            e.setStartBefore(a), e.setEndAfter(document.body), t.appendChild(e.cloneContents())
          } else t.appendChild(document.body.cloneNode(!0));
          const s = ["CODE", "PRE", "IMG", "VIDEO", "AUDIO", "SCRIPT", "STYLE", "NOSCRIPT", "META", "LINK", "IFRAME", "NAV", "FOOTER", "SVG", "CANVAS"],
            n = document.createTreeWalker(t, NodeFilter.SHOW_ELEMENT, {
              acceptNode: e => s.includes(e.tagName) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
            }),
            i = [];
          for (; n.nextNode();) i.push(n.currentNode);
          i.forEach((e => e.parentNode?.removeChild(e)));
          let l = "",
            o = !1,
            r = null;
          const c = new Set(["DIV", "P", "H1", "H2", "H3", "H4", "H5", "H6", "LI", "UL", "OL"]),
            d = document.createTreeWalker(t, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null),
            u = (t, a = !1) => {
              if (e > 0 && l.length >= e) return !1;
              const s = e > 0 ? e - l.length : 1 / 0;
              let n = t;
              return t.length > s && (n = "", o = !0), l += n, !o
            };
          e: for (; d.nextNode();) {
            const e = d.currentNode;
            if (e.nodeType === Node.TEXT_NODE) {
              let t = e.textContent.replace(/\s+/g, " " ).replace(/^\s+|\s+$/g, "");
              if (t) {
                if ("BLOCK_START" === r && (t = t.trimStart()), !u(t)) break;
                r = "TEXT"
              }
            } else if (e.nodeType === Node.ELEMENT_NODE) {
              const t = e.tagName,
                a = window.getComputedStyle(e);
              if ("none" === a.display || "hidden" === a.visibility) continue;
              if (c.has(t)) {
                if (l.length > 0 && !l.endsWith("\n") && !u("\n", !0)) break;
                r = "BLOCK_START"
              }
              switch (t) {
                case "LI":
                  if (!u("OL" === e.parentNode.tagName ? `${Array.from(e.parentNode.children).indexOf(e)+1}. ` : "• ")) break e;
                  break;
                case "BR":
                  if (!u("\n", !0)) break e;
                  break;
                case "H1":
                case "H2":
                case "H3":
                case "H4":
                case "H5":
                case "H6":
                  if (!u(`\n${"#".repeat(parseInt(t[1]))}`, !0)) break e
              }
            }
            if (o) break
          }
          let m = l.slice(0, e);
          return m = m.replace(/(#+)\s*\n+/g, "$1\n\n").replace(/(\S)\n(\S)/g, "$1 $2").replace(/\s+$/, "").trim(), o && (m = m.replace(/\s+$/, "") + "..."), m
        } catch (e) {
          throw e
        }
      }(2e3),
      s = e.substring(0, 2e3);
    if (s.length <= 300) throw d.content_tip = t("ai-detector.contet_short"), new Error("content short");
    if (await new Promise((e => setTimeout(e, 500))), u) return;
    const n = await chrome.runtime.sendMessage({
      type: "ai-detector",
      content: s,
      host: location.host
    });
    if (u) return;
    if (1e5 === n.code && n.data.task_id) h.result_timer = setTimeout((() => {
      u || (h.result(n.data.task_id, s), clearTimeout(d.result_timer))
    }), 2e3);
    else {
      if (120008 !== n.code) throw new Error("error");
      d.err = !1, d.limit_tip = t("ai-detector.limit_tips").replace("[href]", `https://autotdk.com${"en"===a.value?"":`/${a.value}`}/ai-detector`), d.limit = !0, l.value = !1, i.globalLoading = !1
    }
  } catch (e) {
    l.value = !1, i.globalLoading = !1, d.err = !1, ["content short", "no h1"].includes(e.message) || (d.err = !0, H.nk.error({
      message: t('message["Operation failed"]'),
      appendTo: w.querySelector(".autotdk_ext_content"),
      customClass: "position-absolute z-4"
    }))
  }
}
Readable service worker sends JSON text to AutoTDKbackground.js
} else if ("ai-detector" === e.type) {
  let o = await fetch(`${t}/api/text/detect-ai?f=seo_search_keyword_tool&h=${e.host}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      source: "seo_search_keyword_tool"
    },
    body: JSON.stringify({
      text: e.content
    })
  });
  o = await o.json(), s(o)
}
05EvidenceTHIRD PARTY LIST
External service receiving the detector submission
  • autotdk.com

    Receives the AI-detector POST at /api/text/detect-ai with submitted page text and the page host.

Updated 17 September 2026hpmllfbpmmhjncbfofmkkgomjpfaocca