Back to skill

Security audit

Keyapi Instagram User Analysis

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to be a real KeyAPI Instagram analysis tool, but it uses an under-scoped generic MCP runner and stores API tokens in a local plaintext file.

Review this before installing. Use a limited KeyAPI token, prefer setting KEYAPI_TOKEN only in your shell instead of entering it at the prompt, delete artifact/.env if it is created, and clear .keyapi-cache when cached Instagram data should not remain on disk. Treat the runner as a generic KeyAPI MCP client, not strictly an Instagram-only wrapper.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run.js:259
Finding

API Token Persisted in a Plaintext Environment File

Content
View full analysis

Vulnerability Details

File Location: scripts/run.js:259-264
Vulnerability Type: Plaintext sensitive-data storage
Risk Level: Medium

Vulnerable Code

js
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 KEYAPI_TOKEN is absent and the program is attached to an interactive terminal, it prompts the user for the bearer token and automatically writes it to .env in the project root. The file is created without an explicit restrictive mode, so its effective permissions depend on the process umask. The audited project also contains no .gitignore rule protecting .env.

The token is therefore retained as a plaintext credential beyond the current execution. This persistence is not required to perform Instagram analysis: the token can remain in the process environment for the duration of the request. Automatic persistence exceeds the minimum credential-storage privilege necessary for the declared functionality.

Attack Path

  1. A user launches the runner without setting KEYAPI_TOKEN.
  2. The runner interactively requests the token.
  3. The user enters a valid KeyAPI bearer token.
  4. The runner writes the token in plaintext to the project-root .env file.
  5. Under a permissive umask, another local account or process reads the file. Alternatively, the unignored file is accidentally committed, archived, or copied.
  6. An attacker extracts the token and submits authenticated requests to KeyAPI as the victim.

This path requires access to the resulting file or to a repository, archive, or backup into which it was copied; the code does not independently transmit the .env file to an attacker.

Impact Assessment

Successful exploitation discloses the victim's KeyAPI bearer token. An attacker could authenticate within the permissions and ...[truncated 529 chars]

Remediation
View remediation

Remediation Suggestions

  1. Do not persist interactively supplied tokens by default. Retain the token only in process memory and require users to set KEYAPI_TOKEN for later runs.
  2. If credential persistence is offered, require explicit user consent through a dedicated option such as --save-token.
  3. Prefer an operating-system credential store or secret-management service instead of a plaintext project file.
  4. If .env support must remain:
    • Create the file with mode 0600.
    • Verify and correct existing file permissions before writing.
    • Avoid overwriting unrelated .env entries.
    • Write atomically through a securely permissioned temporary file.
    • Add .env and other credential files to .gitignore.
  5. Clearly warn users that the file contains a bearer credential and must not be committed, shared, or included in public archives.
  6. Recommend token rotation if the file may already have been exposed.

A minimally hardened creation pattern would explicitly restrict permissions:

js
writeFileSync(envPath, `KEYAPI_TOKEN=${token}\n`, {
  encoding: "utf8",
  mode: 0o600,
  flag: "wx",
});

This example prevents silent replacement and restricts newly created files, but an OS credential store remains the preferred design.

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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (27)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is presented as a bounded Instagram analysis capability, but the operational instructions describe a generic MCP client that can list tools, inspect schemas, and invoke arbitrary remote tools with user-supplied names and parameters. This mismatch is dangerous because users and orchestrators may grant trust based on the narrow description while the actual behavior enables broader remote capability discovery and execution against an external service.

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 61)May include surrounding context.

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

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

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

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/run.js --platform instagram --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 --platform instagram --list-tools

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

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

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

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

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

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

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

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

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

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

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

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

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/run.js --platform instagram --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 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
82% confidence
Finding

The function explicitly prompts for an API token and is designed to persist it locally in plaintext for future runs. While not credential theft, this creates a credential-handling weakness because secrets are written to a predictable file in the skill directory, where they may be exposed through weak permissions, accidental commits, or later file disclosure bugs.

Content

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

js
// ── MCP client ────────────────────────────────────────────────────────────────

/** Prompt for KEYAPI_TOKEN interactively and persist it to .env */
async function promptToken() {
  if (!process.stdin.isTTY) {
    throw new Error(

Credential Access

High
Category
Privilege Escalation
Confidence
96% confidence
Finding

This line writes the provided KEYAPI_TOKEN directly to ROOT/.env in plaintext, creating a persistent secret on disk in a predictable location. In the context of a downloadable skill runner, this is dangerous because secrets may be leaked via filesystem exposure, backups, logs, or accidental version-control inclusion, and the skill's broader-than-advertised scope makes retaining reusable API credentials more sensitive.

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
92% confidence
Finding

The skill requires environment-based authentication and instructs use of a Node runner, but it does not declare any explicit tool scope or allowed-tools boundary. That omission weakens least-privilege controls and makes it easier for a generic execution wrapper to invoke capabilities beyond the narrowly described Instagram workflow, especially since the document also references schema inspection and arbitrary tool execution patterns.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill encourages sending Instagram profile, follower/following, Stories, Highlights, tagged-content, and similarity data to a third-party KeyAPI service and storing results in a local cache, but it does not prominently warn users about that external transmission and retention. This creates privacy and compliance risk because potentially sensitive social graph and ephemeral content data may be disclosed or persist locally without informed consent or handling guidance.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script is a generic MCP tool runner that supports listing all tools, retrieving schemas, and invoking arbitrary tool names supplied at runtime. In a skill marketed as Instagram user analysis, that generic capability allows operators or downstream agents to enumerate and call server-side functions outside the advertised scope, which is a scope-expansion and policy-bypass risk.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The runner defaults to platform=tiktok and constructs the MCP endpoint from a user-supplied platform, even though the skill is presented as Instagram user analysis. This expands the skill's operational scope beyond its declared purpose and can enable unintended access to other platform tools and data, increasing the attack surface and violating least-privilege expectations.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The dependency is specified with a caret range (^1.10.2), which allows newer compatible releases to be installed without explicit review. This weakens build reproducibility and can unintentionally introduce vulnerable or behavior-changing versions through normal dependency resolution.

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
88% confidence
Finding

The manifest references @modelcontextprotocol/sdk without an exact pinned version while known advisories exist for that package, so the actual installed version may be affected and cannot be verified from this file alone. In an MCP skill context, SDK flaws such as cross-client data leakage, ReDoS, or missing DNS rebinding protections could affect confidentiality or availability if a vulnerable release is resolved.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.