Back to skill

Security audit

小红书图文创作

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Xiaohongshu content skill, but it needs review because it uses local API credentials, external image services, an internet-exposed callback tunnel, and unsafe download/browser handling without enough scoping.

Review this before installing. Use it only with non-sensitive topics, a scoped Seedream/KIE API key, and a trusted fixed API endpoint. Avoid running the callback tunnel unless you understand what local service it exposes, and prefer a hardened version that validates download URLs, avoids --no-sandbox, escapes HTML text, and documents exactly what data is sent to external services.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/seedream_cover.py:36
Finding

API Credential Can Be Transmitted to an Unvalidated Configurable Endpoint

Content
View full analysis

Vulnerability Details

File Location: scripts/seedream_cover.py, lines 36-41 and 73-94
Vulnerability Type: Credential disclosure through insufficient endpoint validation
Risk Level: Medium

Evidence

python
_CREDENTIALS = _load_credentials()

API_URL = _CREDENTIALS.get("endpoint", "https://ark.cn-beijing.volces.com/api/v3/images/generations")
API_KEY = _CREDENTIALS.get("api_key", "")
MODEL = _CREDENTIALS.get("model", "doubao-seedream-5-0-260128")
python
headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {API_KEY}",
}

req = urllib.request.Request(
    API_URL,
    data=json.dumps(payload).encode("utf-8"),
    headers=headers,
    method="POST",
)

try:
    with urllib.request.urlopen(req, timeout=60) as resp:
        result = json.loads(resp.read().decode("utf-8"))

Technical Analysis

The destination receiving the bearer API credential is loaded directly from the credentials file. The code does not require HTTPS, validate the hostname against an allowlist, reject embedded user information, or constrain redirects.

Consequently, a malformed or malicious configuration can set endpoint to an attacker-controlled server or an unencrypted HTTP endpoint. The script then transmits the Seedream bearer token in the Authorization header.

This issue requires the credentials file to be misconfigured or modified. It is not an unauthenticated remote compromise by itself. Nevertheless, configuration files are frequently provisioned through automation, copied from examples, or modified by users who may not realize that endpoint controls where the secret is sent.

Attack Path

  1. An attacker influences deployment configuration, provisioning instructions, or the contents of /root/.openclaw/credentials/seedream.json.
  2. The attacker sets endpoint to an attacker-controlled HTTPS URL or an unencrypted HTTP URL.
  3. A user invokes `seed ...[truncated 668 chars]
Remediation
View remediation

Remediation Suggestions

  1. Do not make the credential destination freely configurable unless custom endpoints are an explicit requirement.
  2. Pin the API hostname to ark.cn-beijing.volces.com, or enforce a strict allowlist of approved hosts.
  3. Require https and reject HTTP, file, FTP, and other schemes.
  4. Disable automatic cross-origin redirects or validate every redirect target before forwarding the authorization header.
  5. Separate endpoint configuration from secret storage and apply restrictive file permissions such as mode 0600.
  6. Use a narrowly scoped token with quota limits and rotation support.
  7. Fail closed when endpoint validation fails, without including the credential in logs or error messages.
  8. Add tests covering hostile endpoints, insecure schemes, embedded credentials, alternate ports, and redirect-based credential leakage.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/seedream_cover.py:106
Finding

Unrestricted Download URL Enables SSRF, Local File Copying, and Resource Exhaustion

Content
View full analysis

Vulnerability Details

File Location: scripts/seedream_cover.py, lines 106-120
Vulnerability Type: Unvalidated server-controlled URL retrieval
Risk Level: High

Evidence

python
image_url = result["data"][0].get("url")
if not image_url:
    print(f"❌ 未获取到图片 URL: {result}")
    return False

print(f"✅ 图片生成成功,正在下载...")

# 下载图片
try:
    urllib.request.urlretrieve(image_url, output_path)
    size_kb = os.path.getsize(output_path) / 1024
    print(f"✅ 下载完成: {output_path} ({size_kb:.1f} KB)")
    return True
except Exception as e:
    print(f"❌ 下载失败: {e}")
    return False

Technical Analysis

The image URL is supplied by an external API response and passed directly to urllib.request.urlretrieve. No scheme, hostname, port, redirect destination, content type, file signature, or response-size validation is performed.

urlretrieve can retrieve more than ordinary public HTTPS images. Depending on the runtime URL handlers, a malicious response may reference:

  • Internal HTTP services reachable only from the host.
  • Cloud instance metadata endpoints.
  • Loopback or private-network addresses.
  • A local file:// resource.
  • An extremely large response that exhausts disk space.
  • Non-image content subsequently delivered as if it were a generated cover.

The configured output path is then populated with whatever content the URL returns. Because the documented execution environment uses paths under /root, the process may run with elevated filesystem and network visibility.

Attack Path

  1. The configured API endpoint is compromised, impersonated, or intentionally malicious.
  2. It returns a syntactically valid JSON response containing an attacker-selected data[0].url.
  3. The URL points to an internal service, cloud metadata endpoint, local file, or oversized remote object.
  4. The script retrieves the resource without validating the destination or response.
  5. Retriev ...[truncated 999 chars]
Remediation
View remediation

Remediation Suggestions

  1. Accept only https image URLs.
  2. Allowlist the exact image-delivery domains used by the trusted provider.
  3. Resolve the hostname and reject loopback, link-local, private, multicast, reserved, and unspecified IP ranges for both IPv4 and IPv6.
  4. Repeat destination validation after every redirect and prevent redirects to a different origin unless explicitly approved.
  5. Replace urlretrieve with a streamed downloader that enforces connection, read, and total-operation timeouts.
  6. Set a strict maximum response size and abort once the limit is exceeded.
  7. Require an approved image MIME type and verify the downloaded file signature before accepting the output.
  8. Download into a securely created temporary file and atomically rename it only after successful validation.
  9. Run the image client as a dedicated unprivileged account with restricted filesystem and network access.
  10. Apply outbound firewall rules that block metadata services, localhost, and private networks when they are not required.
  11. Delete partial downloads after all failures.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_post.py:44
Finding

User-Controlled HTML Is Rendered with the Browser Sandbox Disabled

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_post.py, lines 44-99
Vulnerability Type: Unsafe HTML construction and unsandboxed browser execution
Risk Level: Medium

Evidence

python
def generateCoverHTML(title: str, subtitle: str, out_path: str):
    """生成封面图 HTML 文件"""
    with open(TEMPLATE_HTML, "r", encoding="utf-8") as f:
        html = f.read()

    html = html.replace("[主标题]", title)
    html = html.replace("[副标题/金句,1-2行]", subtitle)

    with open(out_path, "w", encoding="utf-8") as f:
        f.write(html)
python
chrome_cmd = [
    "google-chrome", "--headless", "--disable-gpu", "--no-sandbox",
    "--screenshot=" + out_png,
    "--window-size=1080,1440",
    "--force-device-scale-factor=2",
    "file://" + html_path
]
r = subprocess.run(chrome_cmd, capture_output=True, text=True, timeout=60)

Technical Analysis

The helper inserts title and subtitle into HTML using direct string replacement without HTML escaping. If the expected placeholders are present, input containing HTML markup can break out of the intended text nodes and introduce active elements, scripts, event handlers, frames, or resource requests.

The resulting local file is rendered with Google Chrome using --no-sandbox. Disabling the browser sandbox removes an important containment layer against browser-engine vulnerabilities and makes malicious active content significantly more dangerous.

In the currently packaged template, the placeholder strings differ from those used by generateCoverHTML, so replacement may fail rather than inject the supplied values. This mismatch limits immediate exploitability through the packaged template, but the unsafe construction and rendering primitives remain present and become exploitable if the placeholders are corrected or another compatible template is used.

Attack Path

  1. The helper is used with a template containing the replacement ...[truncated 1377 chars]
Remediation
View remediation

Remediation Suggestions

  1. Escape title and subtitle values with html.escape(value, quote=True) before inserting them into HTML.
  2. Prefer a template engine with automatic HTML escaping.
  3. Make template placeholders consistent and add tests confirming that values are inserted only as text.
  4. Remove --no-sandbox and run Chrome under an unprivileged dedicated account.
  5. Disable JavaScript for screenshot generation when it is not required.
  6. Block network access from the rendering process and use a restrictive Content Security Policy.
  7. Restrict navigation to an application-created temporary file in a dedicated directory.
  8. Validate that generated HTML contains no scripts, event-handler attributes, frames, external URLs, or unexpected elements before rendering.
  9. Use an image or text-rendering library instead of a general-purpose browser where practical.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

This mismatch becomes security-relevant because the documented behavior suggests one benign content-creation flow while the underlying implementation reportedly accesses local credentials and external image-generation services not declared to the user. Hidden resource access undermines informed consent and can expose secrets or user data to undeclared third parties.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

This mismatch becomes security-relevant because the documented behavior suggests one benign content-creation flow while the underlying implementation reportedly accesses local credentials and external image-generation services not declared to the user. Hidden resource access undermines informed consent and can expose secrets or user data to undeclared third parties.

Content

No source excerpt is available for this finding.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/seedream_cover.py (reported line 61)May include surrounding context.

python
else:
        # 标题严格等于用户确认的标题,不扩展
        prompt = f"小红书封面图,{title},扁平矢量风格,简约几何装饰,暖色调,3:4竖版构图"
    return prompt


def generateImage(prompt: str, output_path: str) -> bool:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill declares no explicit tool scope while its documented workflow requires shell execution, network access, and file writes. This creates an overbroad, under-specified privilege surface where an agent may invoke powerful capabilities without clear authorization boundaries or user awareness.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Broad trigger phrases like common conversational requests increase the chance of unintended activation. In a skill with network access, shell commands, and file writes, accidental invocation can lead to unanticipated external requests or local side effects without clear user intent.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

Allowing unspecified additional trigger phrases makes activation boundaries unclear and difficult to audit. This ambiguity is more dangerous in this skill because activation can cascade into web research, external image generation, and artifact creation on disk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill sends user-supplied topic and related content to external search and image-generation services without a clear warning or consent step. This can leak user prompts, business ideas, or sensitive context to third-party providers, especially when the workflow performs multiple external calls.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring is entirely in Chinese and presents the script usage only in Chinese, which imposes a specific language on users without any opt-in or alternative. This matches the policy category for language/locale constraints expressed in natural language.

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

python
script = "/root/.openclaw/workspace/scripts/smart_search.py"
    cmd = ["python3", script, query, "--max-results", str(max_results)]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        return result.stdout[:3000]
    except Exception as e:
        return f"[搜索失败] {e}"

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

python
# 优先用 puppeteer,没有则用 chrome
    try:
        import json, subprocess
        result = subprocess.run(
            ["google-chrome", "--version"],
            capture_output=True, text=True
        )

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

python
print(f"Chrome: {result.stdout.strip()}")
    except Exception:
        print("⚠️ Chrome 不可用,尝试 chromium...")
        result = subprocess.run(
            ["chromium-browser", "--version"],
            capture_output=True, text=True
        )

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

python
"--force-device-scale-factor=2",
        "file://" + html_path
    ]
    r = subprocess.run(chrome_cmd, capture_output=True, text=True, timeout=60)
    if r.returncode == 0:
        print(f"✅ 封面截图成功: {out_png}")
    else:

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

python
def compressImage(in_path: str, out_path: str):
    """压缩图片"""
    compress_script = "/root/.openclaw/workspace/skills/content-factory/scripts/compress_image.py"
    r = subprocess.run(
        ["python3", compress_script, in_path, out_path, "85"],
        capture_output=True, text=True, timeout=60
    )

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The printed content requirements explicitly require Chinese-length limits and Chinese-style output (e.g. '≤1000字符(中文)') without presenting this as an optional or justified locale setting. This is a natural-language policy issue because the script directs users toward a fixed language/locale behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The top-level docstring and user-facing interface text are entirely in Chinese, and the script continues to emit Chinese-only prompts and errors throughout execution. For a general-purpose image generation script, this imposes a specific language/locale on users without opt-in or documented region-specific justification.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The docstring for buildPrompt states that the cover text should exactly match the confirmed title and that the prompt must not add subtitles, quotes, or extra descriptive text. However, when a subtitle is provided, the implementation explicitly appends it into the prompt, directly contradicting the documented rule.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill instructs creation of output files in the workspace without clearly notifying the user beforehand. Even if the files are benign, silent writes reduce transparency and can surprise users, overwrite expected outputs, or leave residual sensitive content on disk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

This HTML template includes fixed Chinese text strings such as "分享", placeholder copy in Chinese, and "小红书" as the source label. Because the file provides no indication that the language is optional or region-specific, it may violate the policy against forcing a specific language without user opt-in.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.