Back to skill

Security audit

Chart Data Viz

Security checks for vulnerabilities and agentic risk

Overview

This skill is a small local charting helper that writes chart images and history locally, with no evidence of deception, network exfiltration, or privilege-seeking behavior.

Install only if you are comfortable with chart titles, labels, numeric values, and output paths being saved locally under ~/.openclaw/workspace/memory/chart. Avoid using it for highly sensitive datasets unless local chart history storage is acceptable, and consider hardening the temporary-file save logic before shared or multi-user deployments.

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/lib/storage.py:23
Finding
Predictable Temporary File Permits Symlink-Based File Overwrite## Vulnerability Details **File Location**: `scripts/lib/storage.py:23-27` **Vulnerability Type**: Unsafe temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```python def _atomic_save(path, data): ensure_dir() tmp = path + ".tmp" with open(tmp, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) os.replace(tmp, path) ``` ### Technical Analysis The `_atomic_save` function uses a fixed temporary-file name derived by appending `.tmp` to the destination path. It opens that path with the normal `"w"` mode, which neither creates the file exclusively nor prevents symbolic-link traversal. If another local process can write to `~/.openclaw/workspace/memory/chart/`, it can create `charts.json.tmp` as a symbolic link to a file writable by the account running the skill. When the skill saves chart metadata, `open()` follows the symbolic link and truncates the target before writing JSON data. The subsequent `os.replace()` operation does not undo the modification already made through the link. ### Attack Path 1. An attacker obtains write access to the chart storage directory under the victim account. 2. The attacker creates `charts.json.tmp` as a symbolic link to another file that the victim account can write. 3. The attacker waits for or induces execution of `make_chart.py`, which eventually calls `save_charts()`. 4. `_atomic_save()` opens the predictable temporary path in write mode. 5. The operating system follows the symbolic link, truncating the linked target and replacing its contents with chart metadata. 6. The target file is corrupted or overwritten under the privileges of the account running the skill. ### Impact Assessment Successful exploitation permits corruption or overwrite of an arbitrary file writable by the skill's operating-system account. This could cause loss of user data or alter user-level configuration, depending on the selected ta ...[truncated 261 chars]
Remediation
## Remediation Suggestions - Create the temporary file securely and unpredictably in the destination directory with `tempfile.NamedTemporaryFile(delete=False, dir=destination_directory, mode="w", encoding="utf-8")`. - Ensure the temporary file is created exclusively and is not a symbolic link. - Flush buffered data and call `os.fsync()` before atomically replacing the destination when durability is required. - Place `os.replace()` in a guarded workflow and remove the temporary file on failure. - Enforce owner-only permissions on the storage directory and metadata files, such as `0700` for directories and `0600` for files. - Where supported, use secure descriptor-based operations or no-follow semantics to further reduce symbolic-link race risks. A hardened implementation should resemble: ```python import os import tempfile def _atomic_save(path, data): ensure_dir() directory = os.path.dirname(path) tmp_path = None try: with tempfile.NamedTemporaryFile( mode="w", encoding="utf-8", dir=directory, prefix=".charts-", suffix=".tmp", delete=False, ) as f: tmp_path = f.name json.dump(data, f, indent=2, ensure_ascii=False) f.flush() os.fsync(f.fileno()) os.replace(tmp_path, path) tmp_path = None finally: if tmp_path is not None: try: os.unlink(tmp_path) except FileNotFoundError: pass ```
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared description presents a full chart generation engine with data visualization capabilities. The supplied code chunk is limited to storage initialization: it creates chart-related directories and writes an initial JSON metadata file if missing. While storing outputs locally is consistent with part of the description, the primary behavior of this code is setup/initialization rather than chart creation or analysis. This is a material mismatch in actual behavior versus declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description centers on generating charts locally from data and helping users choose appropriate visualizations. The supplied code chunk instead reads stored chart records via load_charts() and prints existing chart metadata and output locations. This is materially different from the declared primary purpose for this chunk: it is an inventory/listing utility for previously created charts, not a chart generation engine. While listing charts could be a supporting function within a broader chart system, taken on its own this code does not match the declared behavior and introduces a distinct capability of enumerating stored chart outputs and paths.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared description centers on chart generation and local storage of produced visualizations, with broad support for visualizing datasets and choosing suitable chart types. The code provided does not generate any charts at all; it only prints a suggestion for either a bar or line chart using basic rules based on label count and an x-axis flag. It also does not read CSV/JSON files as files, does not create visual explanations, and does not store outputs. While chart-type recommendation is one small part of the declared purpose, the primary described capability is materially overstated relative to the actual implementation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
81% confidence
Finding
The skill advertises local storage and output generation, which implies file read/write behavior, but it does not declare any explicit tool scope such as permissions or allowed-tools. That creates an authorization ambiguity: an agent may invoke filesystem-capable code without a clearly bounded contract, increasing the risk of unintended access to workspace data or future scope creep.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The skill description uses very broad activation triggers like 'whenever the user wants to visualize data' or 'decide which chart type fits a dataset best,' which can cause over-invocation on common data-analysis requests. In an agent system, overly broad routing increases the chance the skill accesses or stores user data unnecessarily, especially since it writes artifacts into workspace memory.

Missing User Warnings

Low
Confidence
78% confidence
Finding
The `_atomic_save` function writes JSON data to files under `~/.openclaw/workspace/memory/chart` and replaces the destination atomically, but there is no confirmation prompt, logging, print statement, or explanatory comment/docstring in this file describing that persistent user data will be created or updated. For a code file, this qualifies as a file-write operation lacking visible disclosure.

Static analysis

No suspicious patterns detected.