Back to skill

Security audit

guaikei-douyin-data-stream

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its Douyin data-retrieval purpose, but it needs Review because its token handling, default local archiving, broad triggers, and auth-error messages have concrete disclosure and scoping issues.

Review this skill before installing if you are comfortable sending Douyin URLs, keywords, and your GUAIKEI_API_TOKEN to www.guaikei.com. Expect search/post/comment results to be written under the skill’s logs directory by default, and rotate or scope the token if possible because the implementation sends it in URL query strings. The package does not show local code execution beyond its Node.js CLIs, but the documentation and runtime auth messages do not fully match.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T01 · Skill Instruction Hijacking

Error
Location
src/utils/token.js:23
Finding

Authentication Errors Inject Undisclosed Promotional Content

Content
View full analysis

Vulnerability Details

File Location: src/utils/token.js:23-31; src/utils/request.js:50-57
Vulnerability Type: Undisclosed runtime-output manipulation
Risk Level: High

Vulnerable Code

From src/utils/token.js:23-31:

js
function skillToken(token) {
  if (!isValidToken(token)) {
    utils.printWarn("警告: 你的 GUAIKEI_API_TOKEN 未正确配置,技能已暂停. ");
    utils.printSuccess("\t快速获取通道: www.guaikei.com");
    return "";
  }

  utils.printInfo("已使用配置的私有TOKEN");
  return token;
}

From src/utils/request.js:50-57:

js
} else if (res.statusCode === 401 || res.statusCode === 403) {
  const e = new AuthError(
    "GUAIKEI_API_TOKEN 无效, 请检查环境变量 或 联系微信: 13395823479 获取解决方案",
  );
  e.noRetry = true;
  reject(e);
}

The authentication exception is subsequently included in machine-readable output. For example, src/douyin/search-cli.js:174-201 contains:

js
} catch (error) {
  utils.printError(`搜索失败: ${error.message}`);
  const errorOutput = {
    status: "error",
    error_code: error.code || "UNKNOWN",
    message: error.message,
    timestamp: new Date().toLocaleString(),
    request: {
      command: "search",
      keyword: keyword,
      sort: sort,
      time: time,
      duration: duration,
      content: content,
      limit: limit,
    },
    metadata: {
      skill_version: constants.VERSION,
      runtime_version: process.versions.node,
      execution_time: Date.now() - startTime,
    },
    results: null,
  };
  const exitCode = error.name === "AuthError" ? 3 : 1;
  process.stdout.write(JSON.stringify(errorOutput, null, 2) + "\n", () =>
    process.exit(exitCode),
  );
  return;
}

Technical Analysis

The declared Skill behavior requires neutral authentication errors and explicitly states that runtime output must not contain websites, contact details, or marketing language. The implementation violates t ...[truncated 1981 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace both authentication messages with a fixed, neutral message such as: GUAIKEI_API_TOKEN is missing, malformed, expired, or unauthorized. Check the configured environment variable.
  2. Remove all websites, phone numbers, social-media contacts, sales language, and acquisition instructions from runtime output.
  3. Create a centralized authentication-error formatter so every CLI returns the same neutral error code and message.
  4. Do not copy arbitrary or remotely influenced error.message values directly into stdout. Map known exceptions to locally controlled messages.
  5. Keep support and token-acquisition details only in explicitly labeled documentation.
  6. Add automated tests asserting that missing-token and HTTP 401/403 paths contain no URLs, contact numbers, or promotional phrases.
  7. Ensure stdout remains schema-compliant JSON and stderr contains only operational diagnostics.

T09 · Insecure Skill Coding Practices

Warning
Location
src/utils/request.js:94
Finding

API Credential Is Transmitted in URL Query Strings

Content
View full analysis

Vulnerability Details

File Location: src/utils/request.js:94-120,123-140; src/api/search.js:59-76,104-122; src/api/comment.js:38-54,67-81; src/api/post.js:23-37,50-63; src/api/hot.js:19-22
Vulnerability Type: Sensitive credential exposure through URLs
Risk Level: Medium

Vulnerable Code

The generic request implementation serializes every parameter, including the API token, into the URL. From src/utils/request.js:94-120:

js
async function postJson(path, params, data) {
  if (!path || typeof path !== "string") {
    throw new SkillError("PATH_INVALID", "path 必须是非空字符串");
  }
  if (!params || typeof params !== "object") {
    throw new SkillError("PARAM_INVALID", "params 必须是对象");
  }
  if (!data || typeof data !== "object") {
    throw new SkillError("DATA_INVALID", "data 必须是对象");
  }
  params.skill_name = skillName();

  const fullPath = `${path}?${querystring.stringify(params)}`;
  const jsonData = JSON.stringify(data);

  const options = {
    host: constants.BASE_URL,
    path: fullPath,
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Accept-Encoding": "identity",
      "Content-Length": Buffer.byteLength(jsonData),
    },
  };

  return await request(options, jsonData);
}

The GET implementation has the same behavior in src/utils/request.js:123-140:

js
async function getJson(path, params) {
  if (!path || typeof path !== "string") {
    throw new SkillError("PATH_INVALID", "path 必须是非空字符串");
  }
  if (!params || typeof params !== "object") {
    throw new SkillError("PARAM_INVALID", "params 必须是对象");
  }
  params._ = Date.now();

  const fullPath = `${path}?${querystring.stringify(params)}`;
  const options = {
    host: constants.BASE_URL,
    path: fullPath,
    method: "GET",
    headers: { "Accept-Encoding": "identity" },
  };

  return await request(options);
}

A repre ...[truncated 2925 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove token from all query-parameter objects.
  2. Transmit the credential in an HTTP authorization header, for example:
    js
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
      "Accept-Encoding": "identity",
    }
    
  3. Refactor requestApi(), postJson(), and getJson() to accept credentials separately from ordinary query parameters.
  4. Add a redaction utility that replaces token values in errors, traces, and diagnostics before they are logged.
  5. Configure reverse proxies and monitoring systems to suppress or redact sensitive query parameters during migration.
  6. Rotate all existing tokens because prior URL-based transmission may have placed them in retained logs.
  7. Add tests that inspect generated request paths and fail if fields named token, key, secret, or authorization appear in the query string.
  8. Prefer short-lived, scoped tokens so any future disclosure has limited duration and impact.

T09 · Insecure Skill Coding Practices

Note
Location
src/config/constants.js:19
Finding

Query Operations Can Retry Up to Twenty Times Contrary to the Declared Limit

Content
View full analysis

Vulnerability Details

File Location: src/config/constants.js:19-29; src/utils/retry.js:11-30
Vulnerability Type: Excessive retry and resource-consumption behavior
Risk Level: Low

Vulnerable Code

From src/config/constants.js:19-29:

js
/**
 * 查询任务最大重试次数
 */
QUERY_MAX_ATTEMPTS: 20,

/**
 * 重试间隔时间 (毫秒)
 */
RETRY_INTERVAL: 2000,

From src/utils/retry.js:11-30:

js
async function withRetry(fn, maxAttempts, errorHandler) {
  let lastError;
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    try {
      return await fn(attempt);
    } catch (error) {
      lastError = error;
      if (error && error.noRetry) {
        throw error;
      }
      if (errorHandler) errorHandler(attempt, error);
      if (attempt < maxAttempts - 1) {
        const delay = Math.min(
          Math.pow(2, attempt) * constants.RETRY_INTERVAL,
          60000,
        );
        await new Promise((resolve) => setTimeout(resolve, delay));
      }
    }
  }
  throw lastError || new Error(`重试${maxAttempts}次后失败`);
}

Query APIs pass this twenty-attempt value to the retry implementation. For example, src/api/search.js:115-123 contains:

js
const response = await requestApi(
  "GET",
  "/api/douyin/general-search/info",
  params,
  null,
  constants.QUERY_MAX_ATTEMPTS,
  "查询任务",
);

Technical Analysis

The Skill documentation states that network timeouts stop after no more than three attempts. Creation operations use three attempts, but query operations use QUERY_MAX_ATTEMPTS, which is configured as twenty.

withRetry() applies exponential backoff capped at 60 seconds. A persistently retryable error can therefore cause many outbound requests and a long-running invocation. The request layer also uses a 20-second timeout for each attempt, further increasing the potential execution duration.

The code does stop immediately ...[truncated 1393 chars]

Remediation
View remediation

Remediation Suggestions

  1. Reduce QUERY_MAX_ATTEMPTS to three if these operations are genuine error retries.
  2. Separate task polling from error retry behavior. A pending task response should use an explicit polling state rather than exceptions.
  3. Establish a total execution deadline in addition to a per-request timeout.
  4. Establish a maximum outbound-request budget shared across task creation and result retrieval.
  5. Retry only transient failures such as selected timeout and connection-reset errors.
  6. Do not retry malformed responses, deterministic API validation failures, quota errors, or authorization failures.
  7. Honor Retry-After when the service returns a supported throttling response.
  8. Add randomized jitter to backoff delays to reduce synchronized retry traffic.
  9. Add tests confirming that timeout scenarios perform no more than the documented number of attempts and terminate within the declared time budget.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (50)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The skill states that logs are automatically archived to logs/ with filenames containing timestamp, keyword, parameters, and type, but the user-facing description does not prominently warn that retrieved public data and user query parameters will be persisted locally. This creates a privacy and data-retention risk because potentially sensitive research topics, monitored accounts, or collected results may remain on disk unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill states that logs are automatically archived to logs/ with filenames containing timestamp, keyword, parameters, and type, but the user-facing description does not prominently warn that retrieved public data and user query parameters will be persisted locally. This creates a privacy and data-retention risk because potentially sensitive research topics, monitored accounts, or collected results may remain on disk unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The skill states that logs are automatically archived to logs/ with filenames containing timestamp, keyword, parameters, and type, but the user-facing description does not prominently warn that retrieved public data and user query parameters will be persisted locally. This creates a privacy and data-retention risk because potentially sensitive research topics, monitored accounts, or collected results may remain on disk unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill states that logs are automatically archived to logs/ with filenames containing timestamp, keyword, parameters, and type, but the user-facing description does not prominently warn that retrieved public data and user query parameters will be persisted locally. This creates a privacy and data-retention risk because potentially sensitive research topics, monitored accounts, or collected results may remain on disk unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill states that logs are automatically archived to logs/ with filenames containing timestamp, keyword, parameters, and type, but the user-facing description does not prominently warn that retrieved public data and user query parameters will be persisted locally. This creates a privacy and data-retention risk because potentially sensitive research topics, monitored accounts, or collected results may remain on disk unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill states that logs are automatically archived to logs/ with filenames containing timestamp, keyword, parameters, and type, but the user-facing description does not prominently warn that retrieved public data and user query parameters will be persisted locally. This creates a privacy and data-retention risk because potentially sensitive research topics, monitored accounts, or collected results may remain on disk unexpectedly.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
81% confidence
Finding

The skill explicitly depends on the environment variable GUAIKEI_API_TOKEN, but the manifest does not declare a corresponding tool scope or permissions boundary. This creates an undeclared capability mismatch: the runtime will access sensitive environment data without an explicit least-privilege declaration, making review and enforcement harder.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill is designed to trigger even when the user does not mention Douyin, based on broad phrases like '查一下' or '帮我看看' about short-video content, creators, comments, or trends. This overbroad invocation logic can cause the agent to send user requests to an external third-party data service without sufficiently clear user intent, increasing the risk of unintended data disclosure and surprising tool activation.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The implicit trigger examples remain ambiguous for generic requests like short-video competitive research or hot-topic discovery, which may apply to multiple platforms or may not imply consent to query Douyin. In context, this makes accidental invocation more likely and can route user intent to the wrong external service while logging the request locally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill discloses that logs are archived, but it does not present a clear, user-facing warning early enough that retrieved public data and query parameters are automatically stored. Because the skill handles research topics, account URLs, keywords, and possibly large result sets, silent persistence can expose sensitive investigative interests or create avoidable retention/compliance issues.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README advertises automatic JSON log export and later states logs are saved under a local 'logs' directory, but it does not clearly foreground the privacy and retention implications of storing scraped comments, author data, or search results on disk. Even if the data is public, automatic persistence can create compliance, confidentiality, and over-collection risks, especially on shared systems or managed agent environments.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The natural-language trigger mapping includes very broad everyday phrases such as '查一下' and '帮我看看' for Douyin-related tasks, which can cause the skill to activate when a user did not explicitly intend to scrape Douyin data. In an agent setting, this creates a scope-confusion risk that may lead to unexpected third-party API calls, unnecessary token use, and unintended collection/storage of public video or comment data.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The intent recognition rules rely on broad activation keywords like '热搜', '搜索', '评论', '视频', '主页', and '博主' with limited scope constraints, increasing the chance of misrouting ordinary user requests into this scraping skill. Because the skill performs external data retrieval and writes logs, accidental invocation can expose user intent to an external service and create unnecessary local retention of fetched data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The changelog title is primarily presented in Chinese, and the file provides no indication that users may choose another language or locale. Under the policy, language constraints should be optional or explicitly justified; here no such opt-in or justification is present.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The function sends a token and user-supplied video URL to a remote API via requestApi, which is a network operation involving potentially sensitive data. Although the code has developer-oriented docstrings, there is no user-facing disclosure, confirmation, or visible warning in this file about transmitting that data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This function performs a GET request that includes the token and video URL as request parameters, which transmits user or system data over the network. The file documents the parameters for developers, but it does not provide a user-visible warning or disclosure about this external transmission.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The CLI writes full comment results to a local JSON file automatically without explicit user consent or visibility into where long-term data is being stored. Because comment content may contain personal data, usernames, or other sensitive text, silent persistence increases privacy and data-retention risk, especially on shared systems or agent environments.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The CLI persists the full fetched Douyin results to a local JSON file after completing a search operation, even though the skill is described as a retrieval/search tool rather than a data-retention tool. This creates an undisclosed data-at-rest channel that may store personal data, comments, or scraped content on disk where other local users, processes, or later workflows could access it.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The command silently writes retrieved results to a local file without any user-facing disclosure or explicit consent mechanism. For a skill positioned as a search/retrieval utility, this hidden side effect increases privacy and compliance risk because users may reasonably expect transient output only, not persistent local storage.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The CLI writes full search results to a local JSON file after completing the search, which introduces data persistence beyond the user-facing expectation of retrieval. This can expose searched topics and returned content to other local users, backup systems, or later forensic access, especially on shared hosts or agent runtimes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

Persisting search results to local storage without a clear user-facing warning creates a privacy and data-handling risk. Even if the content is 'just search data,' it may reveal user interests, investigative topics, or sensitive operational queries, and the filename also embeds the keyword, increasing exposure.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code hardcodes user-facing error messages in Chinese, including authentication and network failure text, with no indication that the skill supports user language choice or is intentionally limited to a Chinese-speaking context. That creates a natural-language policy issue because the skill effectively forces a specific language on users without opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The module name, comments, and runtime warning/info messages are entirely in Chinese, including the user-facing outputs at L25-L30. Under the policy, forcing a specific language without user choice is a natural-language policy violation unless the locale constraint is explicitly justified, which is not documented here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code constructs all user-facing messages in Chinese, including headings and labels, with no indication that the skill is China-specific or that users can opt into another language. That creates a natural-language policy concern because it forces a locale on users without choice or justification in the file.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

This JSON schema contains user-facing natural-language fields in Chinese only, including the title and description. Under the policy rule, forcing a specific language without user opt-in is a natural-language locale violation unless the regional constraint is explicitly documented and justified, which is not present here.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.