Back to skill

Security audit

ynu-papergraphgeneration-openclaw

Security checks for vulnerabilities and agentic risk

Overview

This paper-diagram skill is mostly aligned with its stated purpose, but it includes an unsafe path for running generated Python code and under-scoped external image download behavior that users should review before installing.

Review carefully before installing. Do not use this skill on confidential, unpublished, or proprietary papers unless you are comfortable sending paper-derived content to the configured image API. Avoid using the generated-chart execution path unless the Python code is shown to you first and run inside a sandbox with no secrets or broad filesystem access.

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

Error
Location
scripts/chart_generator.py:111
Finding
Unsandboxed Execution of Generated Python Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/chart_generator.py:111-133` **Vulnerability Type**: Arbitrary code execution through untrusted generated code **Risk Level**: High ### Vulnerable Code ```python def execute_chart_code(code: str) -> tuple: """ Execute generated Matplotlib code and return (success, output_path, error). """ import os, sys, tempfile try: output_dir = os.path.join(os.path.expanduser("~"), ".qclaw", "workspace", "outputs") os.makedirs(output_dir, exist_ok=True) # Write the code to a temporary file and execute it code_path = os.path.join(output_dir, "_temp_chart.py") with open(code_path, "w", encoding="utf-8") as f: f.write(code) import subprocess result = subprocess.run( ["python", code_path], capture_output=True, text=True, timeout=60, cwd=output_dir ) ``` ### Technical Analysis The `execute_chart_code` function accepts a Python source string, writes it to a predictable local file, and executes it using the system Python interpreter. There is no validation of the source code, AST inspection, import allowlist, operating-system sandbox, privilege restriction, network isolation, or user confirmation. The surrounding chart-generation module builds prompts asking an LLM to produce executable Python. If attacker-controlled paper content influences the generated response, or if the LLM/API is compromised, the returned chart code can contain arbitrary Python operations instead of only Matplotlib drawing instructions. Using `shell=False` does not mitigate this issue because the attacker controls the contents of the Python file rather than the subprocess command-line arguments. The executed program receives the same filesystem access, environment variables, network access, and operating-system identity as the Agent process. The fixed `_temp_chart.py` filename also cr ...[truncated 1595 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Do not execute LLM-generated source code.** Require the model to return a strict data structure containing chart type, labels, series, values, colors, and captions. Validate it against a schema and render it using fixed, trusted Matplotlib functions. 2. If generated code execution cannot be removed, run it in a disposable, hardened container or micro-VM with: - No Agent secrets or inherited environment variables. - No network access. - A read-only root filesystem. - A dedicated writable output directory. - A nonprivileged UID and GID. - Linux namespace, seccomp, and capability restrictions. - Strict CPU, memory, process, file-size, and execution-time limits. 3. Parse the code with Python's `ast` module and reject imports, attribute access, subprocesses, file access, dynamic evaluation, networking, reflection, and other operations outside a narrowly defined allowlist. AST validation should supplement, not replace, operating-system isolation. 4. Require explicit user approval before running generated code and display the exact source that will execute. 5. Use a securely created unique temporary file or directory rather than the predictable `_temp_chart.py` path, and guarantee cleanup with a `finally` block. 6. Invoke the intended interpreter through `sys.executable` rather than relying on a potentially different `python` executable from `PATH`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/image_generator.py:137
Finding
Server-Controlled Image URL Enables SSRF and Unbounded Downloads<![CDATA[ ## Vulnerability Details **File Location**: `scripts/image_generator.py:137-179` **Vulnerability Type**: Server-side request forgery and unrestricted response download **Risk Level**: Medium ### Vulnerable Code ```python response = session.post(api_url, headers=headers, json=payload, timeout=timeout) response.raise_for_status() data = response.json() elapsed = time.time() - t0 print(f"[ImageGen] Response in {elapsed:.1f}s: {str(data)[:300]}", flush=True) if not data.get("success"): return False, "", f"API error: {data}" # Parse the acedata response format image_url = None for key in ("image_url", "url"): image_url = data.get(key) if image_url: break if not image_url: body = data.get("body", {}) inner = body.get("data") if isinstance(body, dict) else None if inner and isinstance(inner, list): image_url = inner[0].get("image_url") if isinstance(inner[0], dict) else None if not image_url: for k in ("data", "images", "outputs"): arr = data.get(k) if isinstance(arr, list) and arr: item = arr[0] image_url = item.get("image_url") if isinstance(item, dict) else None if image_url: break if not image_url: return False, "", f"No image URL in response: {str(data)[:500]}" print(f"[ImageGen] Image URL found, downloading...", flush=True) img_resp = session.get(image_url, timeout=60) img_resp.raise_for_status() output_path = os.path.join(output_dir, output_filename) with open(output_path, "wb") as f: f.write(img_resp.content) ``` ### Technical Analysis The image-generation API controls the `image_url` field consumed by the Skill. The URL is requested without validating: ...[truncated 2346 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit image downloads only from an explicit allowlist of trusted HTTPS hosts. 2. Parse URLs with a standards-compliant URL parser and reject non-HTTPS schemes, embedded credentials, malformed hosts, and unexpected ports. 3. Resolve the hostname before connecting and reject every loopback, private, link-local, multicast, unspecified, and reserved IPv4 or IPv6 address. 4. Disable redirects, or validate the scheme, host, port, and resolved address again for every redirect hop. 5. Protect against DNS rebinding by ensuring that the actual connected address matches an approved resolution. Where possible, enforce the policy at an egress proxy or firewall. 6. Download with `stream=True`, enforce a small maximum size using both `Content-Length` and counted streamed bytes, and terminate the transfer when the limit is exceeded. 7. Require an approved image MIME type, then decode the result with a trusted image library to verify its actual format before saving it. 8. Set separate connection and read timeouts and limit redirect counts. 9. Prefer APIs that return image bytes directly from the already trusted API origin, eliminating the second server-controlled request. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/image_generator.py:177
Finding
Unvalidated Output Filename Allows Path Traversal and Arbitrary File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/image_generator.py:86-96, 177-179` **Vulnerability Type**: Path traversal and arbitrary file overwrite **Risk Level**: Medium ### Vulnerable Code ```python def generate_image( prompt_dict: dict, api_url: str, api_key: str, model: str = "nano-banana-2", output_filename: str = "paper_diagram.png", output_dir: str = None, skill_dir: str = None, timeout: int = 120 ) -> tuple: ``` ```python output_path = os.path.join(output_dir, output_filename) with open(output_path, "wb") as f: f.write(img_resp.content) ``` ### Technical Analysis The public `generate_image` helper accepts `output_filename` and combines it with `output_dir` using `os.path.join`. It does not reject absolute paths, parent-directory components, directory separators, or symbolic-link targets. If `output_filename` is absolute, `os.path.join` discards the intended `output_dir`. If it contains components such as `../`, the normalized filesystem target can escape the output directory. The file is opened in `wb` mode, so an existing writable file is truncated and replaced with bytes obtained from the remote image URL. The primary `draw.py` workflow currently constructs filenames from fixed metadata and performs limited character replacement, making that particular path less directly exposed. However, `generate_image` is a reusable public helper and can be invoked by other Agent integrations with caller-controlled arguments. ### Attack Path 1. An untrusted caller gains control over arguments passed directly to `generate_image`. 2. The caller supplies an `output_filename` such as `../../target-file` or an absolute filesystem path. 3. The generation endpoint returns a successful response and downloadable content. 4. `os.path.join` constructs a path outside the intended output directory. 5. `open(..., "wb")` truncates or creates the targeted file. 6. Remote-controlled bytes are wr ...[truncated 847 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Accept only a plain basename rather than an arbitrary path: - Reject absolute paths. - Reject `..`. - Reject `/`, `\`, drive letters, and NUL characters. - Permit only a conservative filename character set and approved image extensions. 2. Resolve and validate the destination before writing: ```python base = Path(output_dir).resolve() name = Path(output_filename) if name.is_absolute() or name.name != output_filename: raise ValueError("Invalid output filename") target = (base / name).resolve() if target.parent != base: raise ValueError("Output path escapes the output directory") ``` 3. Generate filenames internally using random identifiers rather than accepting caller-selected filesystem paths. 4. Open new files with exclusive-creation semantics where overwriting is unnecessary. 5. Defend against symbolic links by using platform-supported no-follow flags and by keeping the output directory inaccessible to untrusted local users. 6. Verify the downloaded bytes as a valid image before committing them to the final destination. 7. Write to a securely created temporary file inside the output directory and atomically rename it after all validation succeeds. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (48)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description presents a broad multimodal paper-visualization system with PDF/text ingestion, content understanding across a paper, generation of several scholarly figure types, self-verification, and caption export. The supplied code chunk is much narrower: it focuses specifically on result-chart prompt generation for Matplotlib and optionally executing the produced code to create PNG files. While 'Matplotlib 结果图精确绘图' is one listed feature and this code does support that slice, the actual chunk does not implement the larger stated functions such as PDF extraction, whole-paper analysis, figure generation for architecture/algorithm/motivation diagrams, self-checking, or caption output. Additionally, the code executes generated Python code in a subprocess and writes files under the user's home workspace, which is a meaningful capability not reflected in the declared permissions or description. Therefore the description does not accurately represent what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The description and the code partially align on the core workflow: scanning a paper, identifying candidate visualizations, generating figures, and performing a self-check/regeneration loop. However, there are material gaps. The declared description explicitly claims LaTeX/Word caption output and precise Matplotlib result plotting, neither of which appears in this code chunk. The generation path shown uses build_academic_prompt plus generate_image via an external image API, not explicit plotting or document-caption export. Additionally, PDF-to-text support is claimed, but this file contains an apparent implementation inconsistency: it imports extract_text yet calls extract_text_from_pdf, suggesting the PDF extraction path is not accurately represented in the shown code. Therefore the declared description overstates or misrepresents several capabilities relative to this actual code.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared description presents a broad end-to-end academic paper visualization system with document parsing, content analysis, multiple output modes, validation, and caption/chart generation. The actual code shown is much narrower: it formats prompts for figure types such as teaser, architecture, flowchart, and environment diagrams, sends them to a remote image-generation API, parses the response for an image URL, downloads the image, and stores it. While this partially aligns with the 'diagram generation' portion of the description, it does not implement most of the major advertised capabilities in the declared purpose. Therefore the description materially overstates what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The supplied code chunk is a narrow routing/prompt-construction component, not the described visualization engine itself. Its observable behavior is limited to: (1) asking an LLM to choose among teaser/architecture/flowchart/environment/results, and (2) building an extraction prompt specifying target sections and desired JSON schema. The declared description promises substantially broader end-user capabilities—automatic academic illustration generation, PDF extraction, caption generation, self-audit, and precise Matplotlib plotting—which are absent from this code. While routing is plausibly a supporting subcomponent of such a system, the evaluation asks whether the description accurately represents what this supplied code chunk actually does; here it materially overstates the implemented behavior.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
请直接输出完整的 Python 代码:
"""
    return prompt


def generate_line_chart_code(extraction_result: str) -> str:
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
请直接输出完整的 Python 代码:
"""
    return prompt


def generate_line_chart_code(extraction_result: str) -> str:
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
请直接输出完整的 Python 代码:
"""
    return prompt


def generate_line_chart_code(extraction_result: str) -> str:
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The module's stated purpose is chart generation, but it implements a generic pathway for executing arbitrary Python produced from model output. In this skill context, upstream inputs may contain prompt injection or hostile content from papers/PDF text, making the execution path especially dangerous because attacker-controlled content can steer the generated program.

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill silently writes generated code to a file and executes it without explicit disclosure, confirmation, or trust boundary messaging to the user. This increases the likelihood of unsafe operation because users may believe the system is only generating images, while it is actually running code with the agent's privileges.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
## 提取的信息
{extraction_result}
"""
    return prompt


def build_architecture_topology(extraction_result: str, style: str = "academic") -> str:
Confidence
93% confidence
Finding
The function directly interpolates untrusted extraction_result text into an LLM prompt with no delimiting, escaping, or instruction/data separation. If extraction_result comes from a PDF or user-controlled paper text, an attacker can inject prompt instructions that override the intended Mermaid-generation behavior, causing prompt injection, policy bypass, or malicious output that contaminates downstream rendering or workflows.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
请直接输出 Mermaid 代码:
"""
    return prompt


def build_flowchart_topology(extraction_result: str, style: str = "academic") -> str:
Confidence
94% confidence
Finding
This architecture-topology prompt is especially exposed because it asks for precise code-like Mermaid output while embedding untrusted extraction_result verbatim. A malicious paper or extracted text can inject conflicting instructions, hidden Mermaid constructs, or output-shaping attacks that produce unsafe or deceptive diagrams, and the skill context increases risk because generated diagram code is likely to be rendered or trusted as a faithful architecture summary.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
请直接输出 Mermaid 代码:
"""
    return prompt


def build_environment_topology(extraction_result: str, style: str = "academic") -> str:
Confidence
93% confidence
Finding
The flowchart generator returns a prompt built from raw extracted algorithm text, enabling prompt injection from attacker-controlled source material. This can lead to manipulated flowcharts, instruction override, or generation of malformed or misleading Mermaid that undermines integrity of the output pipeline.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
请直接输出 Mermaid 代码:
"""
    return prompt


def convert_to_topology(figure_type: str, extraction_result: str, style: str = "academic") -> str:
Confidence
92% confidence
Finding
The environment-topology prompt similarly embeds untrusted extracted environment information directly into LLM instructions, making it susceptible to prompt injection and output manipulation. In this skill, that can result in deceptive agent-environment diagrams or malformed Mermaid passed to later components, reducing trustworthiness and potentially affecting any renderer consuming the output.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
| `--api-url` | 生图 API 地址 | 环境变量 |
| `--api-key` | 生图 API 密钥 | 环境变量 |
| `--style` | 学术风格: `cvpr` `neurips` `icml` `nature` | `cvpr` |
| `--skip-check` | 跳过自校核 | False |
| `--section-ref` | 对应论文章节(如 `Section 3.1`) | — |

### 环境变量
Confidence
88% confidence
Finding
A documented --skip-check flag disables the self-verification step that compares generated topology against the source paper. In this context, bypassing validation can lead to misleading or fabricated academic figures being produced and trusted, which is especially risky for research integrity and downstream publication workflows.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The README describes sending paper content through OpenClaw and then to external image-generation services, but it does not warn users that their paper text may leave the local environment and be processed by third parties. This is dangerous because academic manuscripts often contain unpublished research, confidential reviewer-sensitive material, or proprietary data, and undisclosed external transmission can cause privacy, confidentiality, or IP leakage.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
Line L08 states that the system generates a 'pure English Prompt,' which imposes a language requirement. This is a natural-language policy concern because the skill does not provide user opt-in, alternatives, or justification for forcing English output.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The natural-language instructions require English labels and titles and prohibit any Chinese characters, which imposes a language policy on output. Because the file does not provide user opt-in or a documented region-specific justification, this is a language/locale policy violation under the stated rules.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
These prompt strings mandate that all text be in English and forbid Chinese characters. That is a natural-language locale restriction, and the file does not indicate user choice or a justified compliance requirement.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The ablation prompt requires all chart text to be English, which is a language constraint expressed in natural language. No opt-in, locale setting, or documented justification is present in the file.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
f.write(code)

        import subprocess
        result = subprocess.run(
            ["python", code_path],
            capture_output=True,
            text=True,
Confidence
99% confidence
Finding
This code executes LLM-generated Python by writing it to disk and invoking a Python interpreter on it. Because the generated code is derived from untrusted paper/extraction content and there is no sandbox, validation, or capability restriction, an attacker can achieve arbitrary code execution, file access, or network activity on the host.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The module docstring and subsequent interactive text present the workflow entirely in Chinese, and the code continues this pattern in all user-facing prompts and errors. This imposes a specific language choice on users without offering a language option or documenting a justified locale restriction.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill processes user-supplied paper content into prompts and sends them to an external image-generation API, but there is no explicit warning or consent step before network transmission. In this skill's context, papers may be unpublished, proprietary, or embargoed, so silently transmitting derived content off-host can expose sensitive research data to third parties.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The module imports `extract_text` from `pdf_to_text` at L018, and the `execute` docstring states it accepts `.pdf` input. However, when handling PDF files the code calls `extract_text_from_pdf(raw_input)`, which is not defined or imported anywhere in this file. This directly contradicts the documented behavior that PDF input is supported by this implementation.

External Transmission

Medium
Category
Data Exfiltration
Content
print_visualization_table(items)

    # 4. 读取配置
    api_url    = args.get("api_url",    os.environ.get("BANANA2_API_URL",    "https://api.acedata.cloud/nano-banana/images"))
    api_key    = args.get("api_key",    os.environ.get("BANANA2_API_KEY",    os.environ.get("ACEDATA_API_KEY", "")))
    model      = args.get("model",      "nano-banana-2")
    output_dir = args.get("output_dir", str(_SCRIPT_DIR / "outputs"))
Confidence
90% confidence
Finding
The code defaults to a third-party HTTPS endpoint (`api.acedata.cloud`) for image generation, meaning selected paper-derived content is transmitted externally. In the context of an academic paper visualization skill, that can leak confidential manuscript content, internal research descriptions, or sensitive figures if users are not expecting off-platform processing.

Static analysis

No suspicious patterns detected.