Back to skill

Security audit

Keyapi Tiktok Shop Creator Discovery

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly aligned with TikTok Shop creator analysis, but it includes overbroad API-runner behavior and token/data persistence that users should review before installing.

Review this skill before installing. Use a dedicated, revocable KeyAPI token, prefer setting KEYAPI_TOKEN only in your shell or secret manager, avoid interactive token entry unless you are comfortable with a plaintext .env file, do not set KEYAPI_SERVER_URL unless you fully trust the destination, and clear .keyapi-cache if cached creator analytics or audience data should not remain on disk.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • 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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run.js:274
Finding

Bearer Token Can Be Redirected to an Arbitrary Server

Content
View full analysis

Vulnerability Details

File Location: scripts/run.js:59, scripts/run.js:274-279, and scripts/run.js:515-516
Vulnerability Type: Unvalidated authentication endpoint override
Risk Level: Medium

Vulnerable Code

js
const SERVER_BASE = process.env.KEYAPI_SERVER_URL ?? "https://mcp.keyapi.ai";
js
async function connect(serverUrl) {
  let token = process.env.KEYAPI_TOKEN;
  if (!token) token = await promptToken();

  const client = new Client({ name: "keyapi-runner", version: "1.0.0" });
  const transport = new StreamableHTTPClientTransport(
    new URL(serverUrl),
    { requestInit: { headers: { Authorization: `Bearer ${token}` } } }
  );
  await client.connect(transport);
  return client;
}
js
const serverUrl = `${SERVER_BASE}/${values.platform}/mcp`;
const client = await connect(serverUrl);

Technical Analysis

The runner reads KEYAPI_SERVER_URL from the process environment and uses it without validating the URL scheme or destination host. The connect() function then unconditionally attaches the production KEYAPI_TOKEN as a bearer credential to requests sent to that destination.

The documented functionality only requires authentication to https://mcp.keyapi.ai. Allowing an arbitrary environment-selected endpoint to receive the credential exceeds the minimum privileges needed for that functionality. Environment variables may be influenced by shell profiles, CI configuration, wrapper scripts, container configuration, or copied setup commands.

Although sending the token to the legitimate KeyAPI service is necessary and disclosed, sending it to an unrestricted override destination is not.

Attack Path

  1. An attacker gains influence over the execution environment, a wrapper script, CI configuration, or instructions used to launch the Skill.
  2. The attacker sets KEYAPI_SERVER_URL to an attacker-controlled endpoint.
  3. The user invokes --list-tools, --schema, or a tool operation.
  4. The runner constr ...[truncated 743 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove KEYAPI_SERVER_URL support if custom endpoints are not essential.
  2. If overrides are required, parse the URL and enforce:
    • The https: scheme.
    • An explicit allowlist containing mcp.keyapi.ai.
    • Rejection of embedded credentials, unexpected ports, and malformed hostnames.
  3. Never send KEYAPI_TOKEN to a custom or untrusted endpoint.
  4. Require a separate environment variable, such as CUSTOM_MCP_TOKEN, for explicitly approved custom servers.
  5. Require an explicit command-line opt-in before connecting to a non-default server and display the exact destination receiving authentication.
  6. Add automated tests confirming that HTTP URLs, lookalike domains, subdomain confusion, and non-allowlisted hosts are rejected.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/run.js:245
Finding

API Token Is Persisted in a Plaintext Project File

Content
View full analysis

Vulnerability Details

File Location: scripts/run.js:245-265
Vulnerability Type: Insecure plaintext credential storage
Risk Level: Low

Vulnerable Code

js
async function promptToken() {
  if (!process.stdin.isTTY) {
    throw new Error(
      "KEYAPI_TOKEN is not set.\n" +
      "  → Register at https://keyapi.ai/ to obtain a free token, then either:\n" +
      "    export KEYAPI_TOKEN=your_token_here\n" +
      "  → Or create a .env file in the skill directory:\n" +
      "    echo \"KEYAPI_TOKEN=your_token_here\" > .env"
    );
  }
  const { createInterface } = await import("readline");
  const rl = createInterface({ input: process.stdin, output: process.stderr });
  return new Promise((resolve, reject) => {
    process.stderr.write("\nKEYAPI_TOKEN is required. Get yours at https://keyapi.ai/\n\n");
    rl.question("Enter your token: ", (answer) => {
      rl.close();
      const token = answer.trim();
      if (!token) {
        reject(new Error("No token entered. Set KEYAPI_TOKEN and try again."));
        return;
      }
      const envPath = join(ROOT, ".env");
      writeFileSync(envPath, `KEYAPI_TOKEN=${token}\n`, "utf8");
      log(`[token] Saved to ${envPath} — future runs will load it automatically`);
      process.env.KEYAPI_TOKEN = token;
      resolve(token);
    });
  });
}

Technical Analysis

When a user enters a token interactively, the runner automatically stores it in .env under the project root. The file is created without an explicit restrictive mode, and the reviewed project does not include a .gitignore rule protecting .env.

The operation also replaces the entire existing .env file rather than updating only the relevant variable. Persistent plaintext storage is not required for the Skill to authenticate because the token can remain in the process environment or be supplied by a credential manager.

The effective permissions depend on the user's operating-system defaults and umask. Consequently, t ...[truncated 1024 chars]

Remediation
View remediation

Remediation Suggestions

  1. Do not persist interactively entered tokens by default; retain them only in process memory.
  2. Prefer environment injection or an operating-system credential store.
  3. If persistence is explicitly requested by the user:
    • Create the file with mode 0600.
    • Verify ownership and permissions after creation.
    • Refuse to write through symbolic links.
    • Preserve existing .env entries rather than overwriting the file.
    • Write changes atomically.
  4. Add .env to .gitignore and package exclusion rules.
  5. Warn users that the file contains a reusable secret and must not be committed, shared, archived, or included in build artifacts.
  6. Provide documented token revocation and rotation instructions for suspected exposure.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (25)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The documented purpose is narrowly framed as TikTok Shop creator discovery, but the skill behavior described in the finding includes generic MCP tool execution, schema/tool enumeration, local file writes, token persistence, and auxiliary URL-processing behavior. This mismatch is dangerous because users may authorize the skill for a narrow analytics task while it actually enables broader actions that can expose secrets, expand data collection, or interact with unrelated tools and files.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 60)May include surrounding context.

md
node scripts/run.js --list-tools

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 110)May include surrounding context.

md
node scripts/run.js --list-tools

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 113)May include surrounding context.

md
node scripts/run.js --list-tools

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 120)May include surrounding context.

md
node scripts/run.js --list-tools

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 124)May include surrounding context.

md
node scripts/run.js --list-tools

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 127)May include surrounding context.

md
node scripts/run.js --list-tools

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 133)May include surrounding context.

md
node scripts/run.js --list-tools

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 140)May include surrounding context.

md
node scripts/run.js --list-tools

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/run.js (reported line 45)May include surrounding context.

js
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(__dirname, "..");

// ── .env loader ───────────────────────────────────────────────────────────────

(function loadDotenv() {
  const envPath = join(ROOT, ".env");

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/run.js (reported line 106)May include surrounding context.

js
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(__dirname, "..");

// ── .env loader ───────────────────────────────────────────────────────────────

(function loadDotenv() {
  const envPath = join(ROOT, ".env");

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/run.js (reported line 240)May include surrounding context.

js
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(__dirname, "..");

// ── .env loader ───────────────────────────────────────────────────────────────

(function loadDotenv() {
  const envPath = join(ROOT, ".env");

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/run.js (reported line 247)May include surrounding context.

js
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(__dirname, "..");

// ── .env loader ───────────────────────────────────────────────────────────────

(function loadDotenv() {
  const envPath = join(ROOT, ".env");

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/run.js (reported line 48)May include surrounding context.

js
// ── .env loader ───────────────────────────────────────────────────────────────

(function loadDotenv() {
  const envPath = join(ROOT, ".env");
  if (!existsSync(envPath)) return;
  for (const line of readFileSync(envPath, "utf8").split("\n")) {
    const m = line.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/run.js (reported line 248)May include surrounding context.

js
// ── .env loader ───────────────────────────────────────────────────────────────

(function loadDotenv() {
  const envPath = join(ROOT, ".env");
  if (!existsSync(envPath)) return;
  for (const line of readFileSync(envPath, "utf8").split("\n")) {
    const m = line.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

This line writes the API token in plaintext to a predictable .env file in the project directory. In the context of a reusable skill, that is dangerous because secrets can be exposed through local file access, accidental repository commits, or redirection to untrusted infrastructure via the server URL override.

Content

Scanner excerpt · scripts/run.js (reported line 262)May include surrounding context.

js
reject(new Error("No token entered. Set KEYAPI_TOKEN and try again."));
        return;
      }
      const envPath = join(ROOT, ".env");
      writeFileSync(envPath, `KEYAPI_TOKEN=${token}\n`, "utf8");
      log(`[token] Saved to ${envPath} — future runs will load it automatically`);
      process.env.KEYAPI_TOKEN = token;

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill requires access to a sensitive environment variable (KEYAPI_TOKEN) and instructs running Node-based tooling, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an avoidable trust gap: an agent or runtime may grant broader execution capability than users expect, increasing the chance of unintended code execution or secret exposure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill explicitly directs caching creator analytics, sales, audience, and trend data under .keyapi-cache/ without any warning about local persistence, retention, or privacy implications. Even if the data is business-oriented rather than highly sensitive PII, storing demographic and commercial profile data locally can create privacy, compliance, and data-leakage risk on shared machines or in multi-tenant environments.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script is a generic MCP runner that can invoke arbitrary tools and target arbitrary platforms, which exceeds the stated skill scope of TikTok Shop creator discovery. In a skill ecosystem, this broad capability increases attack surface and can enable unintended data access or behavior if the skill is assumed to be narrowly scoped.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

When a user enters KEYAPI_TOKEN interactively, the script writes it to a plaintext .env file without explicit consent or warning. This can expose the token to other local users, accidental commits, backup systems, or downstream tooling that reads workspace files.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The script persists API responses to disk automatically, including potentially sensitive analytics, creator data, or other returned content, without an explicit up-front warning or opt-in. On shared systems or in reused workspaces, this can cause unintended data retention and disclosure.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
92% confidence
Finding

The dependency is specified with a caret range (^1.10.2), which permits automatic installation of newer compatible releases rather than a single exact version. This weakens supply-chain reproducibility and makes it harder to verify whether the installed package includes vulnerable or unexpected code, especially for an MCP-facing skill that may process sensitive client/server data.

Content

Scanner excerpt · package.json (reported line 6)May include surrounding context.

json
"type": "module",
  "engines": { "node": ">=18.0.0" },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.10.2"
  }
}

Unverifiable Dependency: @modelcontextprotocol/sdk has 3 known advisory(ies) (CVE-2026-25536 (@modelcontextprotocol/sdk has cross-client data leak via shared server/transport); CVE-2026-0621 (Anthropic's MCP TypeScript SDK has a ReDoS vulnerability); CVE-2025-66414 (Model Context Protocol (MCP) TypeScript SDK does not enable DNS rebinding protec)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
89% confidence
Finding

The manifest references @modelcontextprotocol/sdk without an exact pinned release, while known advisories exist for some versions of this package. Because the resolved installed version cannot be determined from this manifest alone, there is a real risk that deployments may pick an affected version, exposing the skill to issues such as cross-client data leakage, ReDoS, or DNS rebinding weaknesses in MCP-related communications.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

Listing all available tools on the MCP server and retrieving arbitrary tool schemas are administrative/introspection capabilities, not an obvious requirement for discovering and analyzing TikTok Shop creators. These features broaden what a caller can learn and do with the backend beyond the manifest's stated analytical purpose.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

Allowing KEYAPI_SERVER_URL to override the default endpoint lets the script send the bearer token to any attacker-controlled server. This creates an SSRF-like trust-boundary issue and can directly expose credentials and tool inputs to non-KeyAPI infrastructure.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.