Is 塔塔网申神器 - AI一键求职填简历 safe?

High risk

塔塔网申神器 - AI一键求职填简历 is high risk. Code analysis shows the content script, injected on every page, runs eval() on three JSON fields its own backend can return: hotfix_code, a trigger_data code string, and a value_script. Not observed firing; needs the backend to set one.

塔塔网申神器v0.8.4Chrome Web Store
73Risk

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

Publishers can request a review.

Findings

Job-Toolkit Content Script Runs eval() On Code Its Own Backend Returns

HighunwantedCWE-829Dynamic sandbox

Code analysis shows the content script, injected on every page, runs eval() on three JSON fields its own backend can return: hotfix_code, a trigger_data code string, and a value_script.

What actually happens

You did this

You open the toolkit (toolbar icon, or logging in) on any website with the extension installed.

content.js and entry.js are already injected into every page at document_end, in every frame.

The extension did this

The extension asks its own backend for a JSON config, then passes specific fields from that response straight into eval() inside the page you're on.

Three independent fields can reach eval(): hotfix_code, a named trigger_data code string, and a per-field value_script.

The code that does this

The three eval() sinks, shipped vs. deobfuscated

Readable version

entry.js:317-334 entry()

js/entry.js
if (e.data?.is_hotfix) {  originalEval(e.data.hotfix_code);} else {  showMessageBox("请重新下载插件", ...);  n = true;  closeTooltipModal();  chrome.storage.local.set({ version: null });}

content.js:1825-1848 pageConfigs()

js/content.js
await Promise.all([  request("/resume/unique_page_config", "POST", { site_url: url, site_tag: siteTag }),  request("/resume/page_config_batch_entry", "POST", { site_urls: [url] })]).then(([e]) => {  if (e.data.length > 0) {    globalPageConfig = e.data[0];    if (globalPageConfig.trigger) {      const keys = globalPageConfig.trigger.split(",");      const triggerData = globalPageConfig.trigger_data;      for (const key of keys) {        if (triggerData && triggerData[key]) evalTrigger(triggerData[key].code);      }    }  }});

content.js:494-514 getUserItemDataByKey()

js/content.js
if (fieldKey.includes("@")) {  try {    const valueScript = getValueScriptByKey(fieldKey);    if (valueScript) {      return evalTrigger(        `let itemData = ${JSON.stringify(resolvedValue)}; let currentKey = ${JSON.stringify(fieldKey)};` + valueScript      ), [found, window.itemData, window.skip];    }  } catch (e) { console.log("根据相似文本动态获取数据失败!", e); }}

The three response fields that reach eval()

  • hotfix_code
    "window.__ttHotfixApplied = true;" (illustrative)

    A JavaScript string from the version-check response. Runs immediately via eval() when the same response sets is_hotfix to true.

  • trigger_data[key].code
    "document.querySelector('#apply-btn')?.click();" (illustrative)

    A JavaScript string keyed by a name the same response supplies (page_config.trigger). Runs via eval() for each key that has matching code.

  • value_script
    "window.itemData = itemData.trim().toUpperCase();" (illustrative)

    A JavaScript string attached to one resume field in the formatter config. Runs via eval() with the field's live value and key name in scope.

Check if you're affected

Reads a captured JSON response body from the toolkit's own API and reports which of the three known eval sinks it would trigger, without ever calling eval() itself.

Requires
  • Node.js 18+
detect-eval-sinks.js · js
#!/usr/bin/env node// Static detector for the three eval() sinks in tatawangshen.com's toolkit// responses (entry.js originalEval / content.js evalTrigger). Never calls eval.const fs = require("fs");const path = process.argv[2];if (!path) {  console.error("Usage: node detect-eval-sinks.js <response.json>");  process.exit(1);}const body = JSON.parse(fs.readFileSync(path, "utf8"));const findings = [];// Sink 1: GET /plugin/version -> data.is_hotfix / data.hotfix_codeif (body?.data?.is_hotfix && typeof body.data.hotfix_code === "string") {  findings.push({    sink: "entry.js originalEval(hotfix_code) [GET /plugin/version]",    code: body.data.hotfix_code,  });}// Sink 2: POST /resume/unique_page_config -> data[0].trigger / trigger_data[key].codeconst pageConfig = Array.isArray(body?.data) ? body.data[0] : undefined;if (pageConfig?.trigger && pageConfig?.trigger_data) {  for (const key of String(pageConfig.trigger).split(",")) {    const entry = pageConfig.trigger_data[key];    if (entry?.code) {      findings.push({        sink: `content.js evalTrigger(trigger_data['${key}'].code) [POST /resume/unique_page_config]`,        code: entry.code,      });    }  }}// Sink 3: GET /resume/formatter -> keys[].embedding|similar[].value_scriptconst formatterGroups = [body?.data?.embedding, body?.data?.similar].filter(Boolean);for (const group of formatterGroups) {  for (const item of Array.isArray(group) ? group : []) {    if (item?.value_script) {      findings.push({        sink: `content.js evalTrigger(value_script) [GET /resume/formatter, label='${item.label}']`,        code: item.value_script,      });    }  }}if (findings.length === 0) {  console.log("No eval sink present in this response body.");} else {  console.log(`${findings.length} eval sink(s) would fire:\n`);  for (const f of findings) {    console.log(`- ${f.sink}`);    console.log(`  code: ${f.code}\n`);  }}
How to run it
  1. 1Save a captured response body from any of the three endpoints as response.json.
  2. 2Run: node detect-eval-sinks.js response.json
  3. 3It lists which sink(s) are present and the exact code string each would pass to eval().

Third-party destinations

    • www.tatawangshen.com

    The extension's own backend API. Serves GET /plugin/version, POST /resume/unique_page_config, and GET /resume/formatter, the three responses whose fields reach eval().

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.

What it can do

Permissions this extension asks for, as declared in version 0.8.4. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    <all_urls>

  • Store data in your browser

    storage

  • Act on the current tab, but only after you click the extension

    activeTab

Updated 2 November 2026ldohbgcnonoffldimgdngkojkejibina