Back to skill

Security audit

X Growth Operator / X 增长运营助手

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its X operations purpose, but it needs Review because the local dashboard can trigger live X posts without a server-side approval check and renders imported X data unsafely.

Install only after understanding that this skill can post publicly from your X account when live credentials are configured. Do not run the dashboard with live credentials until server-side approval, authentication/CSRF protection, and safe DOM rendering are added. Keep scripts/.env private, restrict X_API_BASE_URL to official HTTPS X origins, disclose Desearch egress, and use pinned npm dependencies with a lockfile.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
app/static/app.js:211
Finding

Stored DOM Cross-Site Scripting Through Unescaped Dashboard Data

Content
View full analysis
${t("no_data")}`; return; } target.innerHTML = entries .map( ([label, value]) => `
${label} ${value}
` ) .join(""); } ``` A remotely influenced opportunity is also rendered without escaping: ```javascript function renderOpportunities(payload) { const items = payload?.items || []; byId("opportunityCount").textContent = t("items_count", items.length); const target = byId("opportunityList"); if (!items.length) { target.className = "list-block empty-state"; target.textContent = t("no_opportunities"); return; } target.className = "list-block"; target.innerHTML = items .slice(0, 6) .map( (item) => `
${item.source_account || t("unknown")} ${localizeActionType(item.recommended_action)}

${item.score} · ${localizeRisk(item.risk_level)} · ${localizeReadiness((item.algorithm_hints || {}).interaction_readiness || "unknown")}

${(item.text || "").slice(0, 140)}

${ item.recommended_action && item.recommended_action !== "observe" ? `
${t("draft")} ...[truncated 2262 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
app/server.py:104
Finding

Dashboard Live-Execution API Does Not Enforce Explicit User Approval

Content
View full analysis
dict: mission = STORE.load_mission() action = load_action() if not action: raise ValueError("No current action to execute.") execution_output = None preflight_result = None if mode == "x-api": if action.get("action_type") in {"reply", "quote_post"}: preflight_result = preflight_action(action) if preflight_result.get("decision") == "block": raise ValueError(json.dumps(preflight_result, ensure_ascii=False)) execution_output = run_x_action(action) result = { "executed_at": utc_now_iso(), "mode": mode, "mission_name": mission.get("name", ""), "action_id": action.get("id"), "action_type": action.get("action_type"), "target_url": action.get("target_url"), "target_account": action.get("target_account"), "draft_text": action.get("draft_text"), "status": "recorded" if mode == "record-only" else "executed" if mode == "x-api" else "dry_run_executed", } ``` The API route exposes that operation directly: ```python if parsed.path == "/api/execute": mode = str(payload.get("mode", "dry-run")) if mode not in {"dry-run", "record-only", "x-api"}: raise ValueError(f"Unsupported execution mode: {mode}") result = execute_action(mode) self.respond_json({"ok": True, "execution": result, "state": build_dashboard_state()}) return ``` By comparison, the command-line implementation contains a server-equivalent control that the dashboard omits: ```python if action.get("requires_approval", True) and not args.approved: raise SystemExit("Refusing to execute without --approved.") ``` ### Technical Analysis The Skill de ...[truncated 1651 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/x_oauth_cli.js:30
Finding

Arbitrary API Base URL Can Receive OAuth Authorization Data Over Plaintext HTTP

Content
View full analysis
{ const req = transport.request( url, { method, headers, agent, }, ``` ```javascript async function signedRequest(method, path, { query = {}, body = null } = {}) { const credentials = getCredentials(); const baseUrls = getBaseUrls(); let lastError = null; const agent = resolveProxyAgent(); for (const baseUrl of baseUrls) { const url = new URL(path, `${baseUrl}/`); for (const [key, value] of Object.entries(query)) { if (value !== undefined && value !== null && value !== "") { url.searchParams.set(key, value); } } const oauthParams = buildOAuthParams(credentials); const signatureParams = { ...oauthParams }; for (const [key, value] of url.searchParams.entries()) { signatureParams[key] = value; } oauthParams.oauth_signature = buildSignature(method, url.origin + url.pathname, signatureParams, credentials); try { const { status, data } = await requestJson(url, { method, headers: { Authorization: buildAuthHeader(oauthParams), "Content-Type": "application/json", "User-Agent": "x-growth-operator/1.0", }, body, agent, }); ``` ### Technical Analysis `X_API_BASE_URL` is accepted wi ...[truncated 1903 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/package.json:6
Finding

Mutable npm Dependency Installation Without a Lockfile

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/desearch_client.py:20
Finding

Desearch Credential and Query Transmission Uses an Undeclared Network Destination

Content
View full analysis
str: load_local_env() key = os.environ.get("DESEARCH_API_KEY") if not key: raise DesearchError("DESEARCH_API_KEY environment variable not set.") return key def api_request(method: str, path: str, params: dict | None = None, body: dict | None = None): api_key = get_api_key() url = f"{DESEARCH_BASE}{path}" if method == "GET" and params: filtered = {k: v for k, v in params.items() if v is not None and v != ""} if filtered: url = f"{url}?{urlencode(filtered, doseq=True)}" headers = { "Authorization": api_key, "Content-Type": "application/json", "User-Agent": "x-growth-operator/1.0", } data = None if method == "POST" and body: data = json.dumps(body).encode("utf-8") try: req = Request(url, data=data, headers=headers, method=method) with urlopen(req, timeout=60) as response: raw = response.read().decode("utf-8") return json.loads(raw) if raw else {} ``` The declared network list does not include Desearch: ```yaml network_access: - "https://api.twitter.com" - "https://api.x.com" ``` ### Technical Analysis The Desearch integration sends a dedicated `DESEARCH_API_KEY` in the Authorization header and sends user-provided search terms and filters to `https://api.desearch.ai`. This transfer is necessary for the optional live Desearch feature and does not send the X OAuth credentials. However, the Skill's explicit network destination declarations identify only the official X API origins. The actual egress ...[truncated 1280 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (39)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description describes an operational social-growth skill focused on X/Twitter monitoring, mission planning, drafting, ranking opportunities, and action execution. The actual code chunk does none of those things. It is a standalone build script that collects files from specific directories and writes them into a zip archive under dist/. This is a materially different primary purpose and introduces an undeclared capability: packaging repository contents into a distributable artifact. While build tooling can support a skill, this specific code chunk does not implement the described end-user functionality, so the description does not accurately represent the behavior shown.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents an operational X growth assistant whose primary function is planning and executing social growth workflows. The actual code chunk does not implement any of those domain behaviors. Instead, it performs a preflight check of the local runtime environment and required credentials/files for a tool named x-growth-operator. While such checks could support the broader system, this specific code’s primary purpose is environment validation, which is materially different from the declared end-user functionality. No harmful undeclared exfiltration is visible, but the behavior is still a mismatch because it focuses on local dependency and credential availability rather than the described X growth operations.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The code is narrowly focused on importing/searching X content from Desearch and saving normalized opportunities locally. This partially aligns with the declared monitoring of KOL posts and discussions, and the local audit/output trail. However, the declared description presents a broader operational skill that can plan missions, generate drafts, rank opportunities, and execute approved actions. None of those core capabilities appear in this code chunk. The code’s primary purpose is data ingestion and normalization, which is a subset of the declared description, so the description overstates what this specific code actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description centers on X/Twitter growth operations: monitoring KOL posts, detecting emerging discussions, drafting content, ranking opportunities, and executing approved actions. The supplied code does none of those things. Instead, it reviews a provided feedback JSON, updates a persistent memory store for future cycles, and writes a feedback report. While a local audit/report artifact could be loosely related to operational tooling, the primary purpose here is feedback ingestion and memory updating, which is materially different from the declared mission-driven X growth workflow. Therefore this code chunk does not accurately match the declared description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description promises a full X growth operations workflow with content monitoring, analysis, drafting, prioritization, execution, and audit capabilities. The actual code chunk does none of these things. It is only a stub/placeholder client with a config field and a describe_mode() accessor. This is a material mismatch in primary purpose and implemented capabilities, even though the placeholder comments acknowledge future intent.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a higher-level growth-operations tool focused on monitoring, analysis, mission generation, drafting, ranking, and audited execution. The supplied code does not implement those functions. Instead, it is a low-level command-line client for authenticated X API calls: posting tweets/threads and retrieving the current user or a specific tweet. While posting approved actions is loosely related to execution on X, the primary behavior is materially narrower and different from the declared end-to-end growth operations capability. Therefore this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
88% confidence
Finding

The skill is designed to consume high-value OAuth credentials from environment variables and a local scripts/.env configuration, creating direct credential-access capability. In combination with shell, file, and network access, any unsafe script behavior, logging mistake, or prompt-influenced command path could expose or misuse the X account credentials to post content or access account data.

Content

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

md
- node
        - npm
      config:
        - scripts/.env
    primaryEnv: X_API_KEY
    install:
      - kind: node

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

The skill explicitly designates scripts/.env as a local state path while also requiring live X API credentials and enabling subprocess execution. Storing or persisting secrets in a writable workspace path increases the chance that child processes, logs, packaging, or other skill components can read, copy, or accidentally expose those credentials, turning long-lived API tokens into accessible local material.

Content

Scanner excerpt · agents/openai.yaml (reported line 22)May include surrounding context.

yaml
uses_subprocesses: true
  local_state_paths:
    - "data/"
    - "scripts/.env"

policy:
  allow_implicit_invocation: false

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/build_skill_bundle.py (reported line 26)May include surrounding context.

python
def load_local_env() -> Path | None:
    env_path = SCRIPTS_DIR / ".env"
    if not env_path.exists():
        return None

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/common.py (reported line 13)May include surrounding context.

python
def load_local_env() -> Path | None:
    env_path = SCRIPTS_DIR / ".env"
    if not env_path.exists():
        return None

Credential Access

High
Category
Privilege Escalation
Confidence
92% confidence
Finding

The script automatically loads secrets from a local .env file in the script directory, creating a higher risk of credential exposure through accidental inclusion in repositories, packaging artifacts, backups, or permissive filesystem access. Because these credentials are write-capable X OAuth tokens, compromise would let an attacker read account data and post content as the account.

Content

Scanner excerpt · scripts/x_oauth_cli.js (reported line 4)May include surrounding context.

js
#!/usr/bin/env node

const path = require("path");
require("dotenv").config({ path: path.join(__dirname, ".env") });

const crypto = require("crypto");
const http = require("http");

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill declares substantial capabilities—environment access, filesystem read/write, network use, and shell execution—but does not define a restrictive tool scope such as permissions or allowed-tools. That increases the blast radius if the skill is invoked unexpectedly or if downstream scripts behave unsafely, because the runtime has no explicit least-privilege boundary at the skill declaration layer.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The trigger conditions are broad enough to match many ordinary social-media or writing requests, which can cause the skill to activate in situations the user did not intend. In a skill that can write files, use credentials, access the network, and eventually perform authenticated posting, over-broad invocation increases the risk of unintended sensitive actions or unnecessary exposure of local/project data to supporting scripts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The /api/execute endpoint can trigger real external X actions when mode is set to "x-api", but this server-side code does not enforce any authentication, authorization, CSRF protection, or explicit confirmation token before execution. In the context of a growth-operations skill designed to post/reply on X, an attacker or unintended local process that can reach the service could cause unauthorized posts or replies, making the lack of a gated confirmation path materially risky.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The reply-generation logic automatically chooses Chinese if the mission goal or topic contains CJK characters and otherwise produces English. This imposes a language choice derived from input text rather than explicit user opt-in, which is a natural-language locale policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The quote-post builder switches between Chinese and English solely by detecting CJK characters in the mission goal or topic label. Because no user choice or opt-in is consulted, the skill enforces a locale behavior implicitly.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The post builder chooses Chinese whenever CJK is detected and English otherwise, with no explicit mechanism for user language selection. This is a natural-language policy issue because the skill dictates output locale based on heuristic detection rather than stated user preference.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This function selects Chinese or English output by checking for CJK characters in mission fields. That behavior forces a locale choice without any opt-in or override path, which matches the language-policy violation criteria.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · core/execution.py (reported line 45)May include surrounding context.

python
def run_x_cli(args: list[str]) -> dict:
    completed = subprocess.run(
        ["node", "scripts/x_oauth_cli.js", *args],
        check=False,
        capture_output=True,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

run_x_action performs a real posting subprocess immediately from an action object, and this file contains no built-in confirmation gate, dry-run safeguard, or provenance check that the action was explicitly approved by the user. In an agent skill designed to monitor posts and execute actions, that makes unintended or adversarially induced posting more dangerous because a malformed plan or prompt injection elsewhere could directly cause public account activity.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · core/execution.py (reported line 62)May include surrounding context.

python
def run_x_action(action: dict) -> str:
    cmd = build_x_command(action)
    completed = subprocess.run(cmd, check=False, capture_output=True, text=True)
    if completed.returncode != 0:
        if completed.stderr.strip():
            raise SystemExit(completed.stderr.strip())

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The parser explicitly supports English and Chinese trigger/section labels, including multiple hard-coded Chinese phrases, but there is no user-facing indication that locale handling is optional or scoped to a Chinese-specific use case. Under the policy rule, forcing or implicitly privileging a specific language/locale without opt-in can be a natural-language policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The title and entire instruction set are written as a Chinese-only brief, which implicitly constrains the skill to a specific language/locale. The file does not provide any user opt-in, alternative language option, or justification that this is a region-specific skill, which can violate language/locale policy requirements.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/check_env.py (reported line 52)May include surrounding context.

python
if not failed:
        try:
            version = subprocess.run(["node", "-v"], check=False, capture_output=True, text=True).stdout.strip()
            if version:
                print(f"Node version: {version}")
        except Exception:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

In x-api mode, this script can trigger real outbound actions against X via run_x_action(action). While it requires the --approved flag, there is no independent verification that approval came from a trusted interactive user flow, and the action can be executed directly from CLI-supplied files without an explicit runtime warning or stronger confirmation barrier. In the context of a growth-operations skill designed to execute social-media actions, this increases the risk of unintended or unauthorized posting if upstream state or automation is compromised.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.