Back to skill

Security audit

XHS Viral Content Factory

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly does what it claims, but it can send selected local documents and an API key to an arbitrary LLM endpoint without enforcing the HTTPS/provider-only safety claims in its documentation.

Install only if you are comfortable sending the selected files and optional posting history to your chosen LLM provider. Set LLM_BASE_URL yourself, use HTTPS provider URLs, avoid untrusted or plaintext HTTP endpoints, and do not point --source or --history at sensitive folders or files.

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/generate_xhs.py:68
Finding

Unvalidated API Endpoint Can Expose Credentials and Local Document Content

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_xhs.py:68-79 and scripts/generate_xhs.py:99
Vulnerability Type: Unrestricted destination and transport for sensitive network transmission
Risk Level: Medium

Vulnerable Code

python
headers = {
    "Authorization": f"Bearer {api_key}",
    "Content-Type": "application/json"
}

url = base_url.rstrip('/') + "/chat/completions"

payload = {
    "model": model_name,
    "messages": [
        {"role": "system", "content": "You are a professional social media manager specializing in Xiaohongshu (XHS). Output in Chinese language."},
        {"role": "user", "content": prompt}
    ],
    "temperature": 0.7
}

try:
    response = requests.post(url, headers=headers, json=payload, timeout=60)

The endpoint is populated directly from an environment variable:

python
BASE_URL = os.getenv("LLM_BASE_URL") or "https://api.openai.com/v1"

Technical Analysis

The script allows LLM_BASE_URL to control the destination of an outbound request without validating its scheme or hostname. The request contains two sensitive data classes:

  1. The LLM API credential in the Authorization header.
  2. Content read from user-selected source files and optional publishing history in the request body.

Although the documented default uses HTTPS, the implementation accepts arbitrary values, including plaintext http:// endpoints and attacker-controlled hosts. This conflicts with the documentation's assurance that local content is transmitted through encrypted HTTPS.

The outbound LLM request is necessary for the declared content-generation functionality. However, accepting an unrestricted destination and insecure transport exceeds the minimum network privilege required. An HTTPS-only restriction is appropriate for remote providers. If local HTTP inference endpoints must be supported, they should require explicit opt-in and must not receive production API credentials.

Attack Path

  1. A ...[truncated 1523 chars]
Remediation
View remediation

Remediation Suggestions

  1. Parse LLM_BASE_URL with urllib.parse.urlparse and reject malformed URLs.
  2. Require the https scheme for all remote endpoints.
  3. Reject URLs containing embedded usernames or passwords.
  4. Maintain an optional allowlist of approved provider hostnames, or require explicit user confirmation before sending data to a non-default hostname.
  5. Disable automatic redirects or validate every redirect destination, ensuring redirects cannot downgrade HTTPS or move sensitive requests to an unapproved host.
  6. If localhost HTTP endpoints are required for tools such as Ollama, allow only loopback addresses through an explicit opt-in setting and avoid sending an API key when authentication is unnecessary.
  7. Display the resolved destination hostname and clearly warn the user that selected source and history content will be transmitted before the first request.
  8. Update the security documentation so its transport guarantees exactly match the enforced implementation.

Example validation logic:

python
from urllib.parse import urlparse

def validate_base_url(base_url):
    parsed = urlparse(base_url)

    if parsed.scheme != "https":
        raise ValueError("LLM_BASE_URL must use HTTPS")

    if not parsed.hostname:
        raise ValueError("LLM_BASE_URL must contain a valid hostname")

    if parsed.username or parsed.password:
        raise ValueError("Embedded URL credentials are not allowed")

    return base_url.rstrip("/")

Use the validated value to construct the endpoint, and configure redirect handling conservatively:

python
validated_base_url = validate_base_url(base_url)
url = validated_base_url + "/chat/completions"

response = requests.post(
    url,
    headers=headers,
    json=payload,
    timeout=60,
    allow_redirects=False,
)
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • 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 (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The manifest and description overstate capabilities and omit important operational behavior, including external API usage and limitations around PDF extraction, pattern selection, and image generation. This can mislead users into granting trust or supplying sensitive documents under false assumptions about what the skill does and where data is processed.

Content

No source excerpt is available for this finding.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

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

md
---
name: xhs-viral-factory
description: 小红书爆款图文全自动生产工厂。支持从 PDF、Markdown 或文件夹提取内容,自动匹配治愈、干货、反直觉、视觉流 4 大模式。
version: 1.1.3
metadata:
  openclaw:
    emoji: "rocket"
    requires:
      env:
        - LLM_API_KEY
    primaryEnv: LLM_API_KEY
    install:
      - id: requests
        kind: pip
        package: requests
        label: "安装网络请求依赖 (requests)"
---

# 🚀 XHS Viral Factory: 小红书内容全自动生产工厂

**不再为“发什么”和“怎么写”发愁。把你的原始素材丢给它,剩�

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill declares environment requirements and describes behavior that reads local files, writes output, and sends content to an external LLM API, but it does not declare an explicit tool/permission scope. This weakens user visibility and policy enforcement around sensitive capabilities, especially because local document contents are exfiltrated to a remote provider.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The natural-language content of the skill, including its name, description, and usage guidance, is presented exclusively in Chinese. Under the language/locale policy, forcing a specific language without user opt-in can be a policy violation when no alternative language choice is offered.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file’s operational instructions and persona are presented only in Chinese, which can force a specific language/locale on users without opt-in. The policy allows language constraints only when user choice is offered or the locale limitation is clearly documented and justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The system message hard-codes "Output in Chinese language," which imposes a specific language on all outputs. This is a natural-language policy concern because the file provides no user opt-in, language selection mechanism, or documented region-specific justification for the restriction.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
93% confidence
Finding

The script sends source_content, style data, and optional publishing history to an external LLM endpoint via requests.post. Because the source path can point to arbitrary local files or folders, this can exfiltrate sensitive local content to a third-party service, especially when users do not fully understand what will be uploaded.

Content

Scanner excerpt · scripts/generate_xhs.py (reported line 84)May include surrounding context.

python
}

    try:
        response = requests.post(url, headers=headers, json=payload, timeout=60)
        response.raise_for_status()
        result = response.json()
        return result['choices'][0]['message']['content']

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/generate_xhs.py (reported line 101)May include surrounding context.

python
# Environment variables handling (Declared in SKILL.md)
    API_KEY = os.getenv("LLM_API_KEY")
    BASE_URL = os.getenv("LLM_BASE_URL") or "https://api.openai.com/v1"
    MODEL = os.getenv("LLM_MODEL") or "gpt-4o"

    if not API_KEY:

Static analysis

No suspicious patterns detected.