Back to skill

Security audit

Usage Visualizer

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its usage-reporting purpose, but it needs review because its local/no-network claims are stronger than the code supports.

Review before installing if you need a strict no-network or high-assurance privacy posture. Use a dedicated virtual environment, set OPENCLAW_WORKSPACE to a directory you control, and be aware that the skill scans local OpenClaw/Clawdbot session logs and maintains a local usage database. The publisher should pin dependencies, narrow the zero-network/audit claims, and escape or validate all log-derived HTML before Chromium rendering.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/html_report.py:148
Finding
Unescaped Session Metadata Allows Active HTML Injection in the Chromium Report Renderer<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/fetch_usage.py:85-90` - `scripts/html_report.py:148-155` - `scripts/generate_report_image.py:67-80` **Vulnerability Type**: HTML injection into a local browser rendering context **Risk Level**: Medium ### Vulnerable Code `scripts/fetch_usage.py:85-90` accepts a model identifier from session logs without validation: ```python # Extract model info model = ( message.get("model") or data.get("model") or data.get("model_alias") or "unknown" ) ``` `scripts/html_report.py:148-155` interpolates that identifier directly into HTML: ```python sorted_period_models = sorted(period_model_tokens.keys(), key=lambda x: -period_model_tokens[x]) for m in sorted_period_models[:6]: tokens = period_model_tokens[m] cost = period_model_cost[m] pct = (tokens / total_tokens * 100) if total_tokens > 0 else 0 u_cost = (cost / tokens * 1000000) if tokens > 0 else 0 html += f"""<div style="margin-bottom:24px"> <div style="display:flex;justify-content:space-between;margin-bottom:8px;font-size:13px"><span style="color:#10b981;font-weight:600">{m[:18]}</span><span>{fmt_tokens(tokens)}</span></div> <div style="height:6px;background:#2a2a2a;border-radius:3px;overflow:hidden;margin-bottom:6px"><div style="height:100%;width:{pct:.1f}%;background:#10b981"></div></div> <div style="font-size:10px;color:#6b7280;display:flex;justify-content:space-between"><span>Unit Cost: ${u_cost:.2f}/M</span><span>Cost: {fmt_cost(cost)}</span></div></div>""" ``` `scripts/generate_report_image.py:67-80` writes and opens the resulting document in Chromium: ```python # Save HTML with open(html_path, "w", encoding="utf-8") as f: f.write(html) print(f"HTML saved to {html_path}") # Generate image with html2image hti = Html2Image() hti.output_path = str(output_dir) # High-resolution PPT viewport (1440p style ratio, increased height for safety) hti.size = (1200, 1000) hti.scree ...[truncated 2781 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape every untrusted value before inserting it into HTML: ```python from html import escape safe_model = escape(str(m), quote=True) ``` Use `safe_model` in the generated markup instead of `m` or `m[:18]`. 2. Prefer a template engine with automatic escaping rather than constructing the document through f-strings. 3. Validate model identifiers when parsing logs. If model names are expected to contain only a limited character set, enforce a conservative allowlist such as letters, digits, spaces, periods, underscores, colons, slashes, and hyphens. 4. Apply escaping to every dynamic HTML field, including the report title and all future values derived from logs or command-line arguments. 5. Configure Chromium to disable JavaScript when it is not required for rendering. 6. Block HTTP, HTTPS, WebSocket, and other external resource requests during rendering. A report intended to be fully local should load only the generated local document and bundled local assets. 7. Run Chromium with an isolated temporary profile and retain its sandbox. Avoid flags that weaken same-origin, file-origin, or sandbox protections. 8. Add regression tests using compact payloads, including active markup shorter than 18 characters, and verify that the output contains escaped text and causes no network requests. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Open-Ended Dependency Versions Make Installation Non-Reproducible<![CDATA[ ## Vulnerability Details **File Locations**: - `requirements.txt:1-3` - `SKILL.md:14-17` **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: Low ### Vulnerable Code `requirements.txt:1-3` specifies only minimum versions: ```text pyyaml>=6.0 html2image>=2.0.0 Pillow>=10.0.0 ``` `SKILL.md:14-17` instructs the platform to install those dependencies: ```yaml install: - id: pip-deps kind: exec command: "pip3 install -r requirements.txt" label: "Install Python dependencies" ``` ### Technical Analysis The dependency specifications permit pip to select any future compatible release. Consequently, the installed code is not necessarily the code that existed when this Skill version was reviewed. Python package installation and import can execute package-controlled code under the installing user's privileges. If a dependency account, release process, distribution artifact, or dependency chain is compromised, the installation command could introduce behavior that was absent during this audit. The reviewed package names appear legitimate, and the repository does not specify an untrusted package index or an apparent typosquatted package. Therefore, this is a supply-chain hardening issue rather than evidence that the current dependencies are malicious. ### Attack Path 1. A future version or transitive dependency of one of the permitted packages is compromised or publishes unsafe behavior. 2. A user or agent installs the Skill dependencies using the documented command. 3. Because the requirements contain open-ended lower bounds, pip resolves the compromised or otherwise unreviewed release. 4. Package-controlled installation or runtime code executes with the privileges of the user running pip or the Skill. 5. The malicious dependency can access files, environment variables, or network resources available to that process. ### Impact Assessment The maximum impact is determined by the privileges of the installati ...[truncated 518 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct dependency to an exact reviewed version: ```text PyYAML==<reviewed-version> html2image==<reviewed-version> Pillow==<reviewed-version> ``` 2. Generate a lock file that includes all transitive dependencies. 3. Record SHA-256 hashes for every distribution and install with pip's `--require-hashes` option. 4. Review dependency updates before changing the lock file, including release notes, ownership changes, and known vulnerability advisories. 5. Install dependencies in a dedicated virtual environment rather than the user's global Python environment. 6. Use a trusted, explicitly configured package index and avoid unexpected fallback indexes. 7. Consider removing `PyYAML` if it is not needed at runtime; `report.py` imports it but does not use it in the reviewed code. Reducing dependencies decreases supply-chain exposure. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (27)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Security-marketing claims such as 'audit-verified privacy' without technical substantiation are dangerous because they can induce misplaced trust and relaxed review. In an agent-executed skill, unsupported assurance language increases the likelihood that users permit access to logs or local files under false assumptions about how data is handled.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Security-marketing claims such as 'audit-verified privacy' without technical substantiation are dangerous because they can induce misplaced trust and relaxed review. In an agent-executed skill, unsupported assurance language increases the likelihood that users permit access to logs or local files under false assumptions about how data is handled.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding
Security-marketing claims such as 'audit-verified privacy' without technical substantiation are dangerous because they can induce misplaced trust and relaxed review. In an agent-executed skill, unsupported assurance language increases the likelihood that users permit access to logs or local files under false assumptions about how data is handled.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding
Security-marketing claims such as 'audit-verified privacy' without technical substantiation are dangerous because they can induce misplaced trust and relaxed review. In an agent-executed skill, unsupported assurance language increases the likelihood that users permit access to logs or local files under false assumptions about how data is handled.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
Security-marketing claims such as 'audit-verified privacy' without technical substantiation are dangerous because they can induce misplaced trust and relaxed review. In an agent-executed skill, unsupported assurance language increases the likelihood that users permit access to logs or local files under false assumptions about how data is handled.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
Security-marketing claims such as 'audit-verified privacy' without technical substantiation are dangerous because they can induce misplaced trust and relaxed review. In an agent-executed skill, unsupported assurance language increases the likelihood that users permit access to logs or local files under false assumptions about how data is handled.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares executable installation and runtime commands plus environment and filesystem usage, but does not define any explicit tool scope or permissions boundary. That increases the chance an agent will run shell/file operations with broader access than users expect, especially because the skill also makes strong safety/privacy claims that may reduce scrutiny.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The skill claims zero network dependencies, but the install step runs 'pip3 install -r requirements.txt', which normally retrieves packages from the network. This inconsistency matters because network access changes the trust model, introduces supply-chain risk, and contradicts the user's expectation of fully local operation.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The manifest frames the skill as providing usage statistics and visual reporting, which suggests analytics over local data. In this file, the code not only fetches/parses session logs but also clears existing records and persists normalized usage data via UsageStore, making it a data-ingestion and storage-modification component rather than just visualization/reporting.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This code clears stored usage records and then writes newly parsed records into persistent storage, but it provides no visible confirmation prompt, print/log disclosure, or other user-facing warning in this file. Because these operations modify local user data and can remove prior records, they meet the missing-disclosure criteria for code files.

Session Persistence

Medium
Category
Rogue Agent
Content
def calc_cache_savings(tokens_summary: dict) -> dict:
    """Calculate cache savings - how much saved with 90% discount on cache reads"""
    cache_read = tokens_summary.get("cache_read_tokens", 0)
    cache_write = tokens_summary.get("cache_creation_tokens", 0)
    
    if cache_read == 0 and cache_write == 0:
        return {"read_savings": 0, "write_cost": 0, "total_savings": 0}
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Intent-Code Divergence

Medium
Confidence
84% confidence
Finding
The docstring presents the tool as a simple one-step reporting runner, but the implementation automatically launches a fetch/sync subprocess unless disabled. In the context of a skill marketed as local and privacy-audited, this hidden default behavior increases the risk of unauthorized or surprising external interaction and can mislead operators about the tool's actual data flow.

Description-Behavior Mismatch

Medium
Confidence
89% confidence
Finding
The skill metadata and description emphasize local-only, privacy-preserving reporting, but the runner performs a fetch/sync by default unless the user explicitly opts out. In a security-sensitive agent context, this mismatch can cause unexpected network access and data transfer, undermining user trust and potentially exposing usage telemetry contrary to expectations.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run(cmd):
    result = subprocess.run(cmd, cwd=BASE_DIR, text=True, capture_output=True)
    if result.returncode != 0:
        return False, result.stdout.strip(), result.stderr.strip()
    return True, result.stdout.strip(), result.stderr.strip()
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The clear_records method performs DELETE operations, including unconditional deletion of all records when no filters are provided, but it provides no confirmation prompt or user-facing warning. The docstring is minimal and does not disclose that calling the method with no arguments erases the entire usage_records table.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The user-facing output strings are fixed in Chinese (e.g. the report title and labels), which enforces a specific language regardless of user preference. This matches the policy category for language or locale constraints without opt-in or documented justification.

Intent-Code Divergence

Low
Confidence
72% confidence
Finding
The manifest advertises 'Audit-verified privacy (No credentials stored)', yet the skill requires the `OPENCLAW_WORKSPACE` environment variable. While this variable is not necessarily a credential, the blanket privacy claim is broader than what the file substantiates and may mislead users about external state dependencies.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pyyaml>=6.0
html2image>=2.0.0
Pillow>=10.0.0
Confidence
97% confidence
Finding
The dependency is specified with a lower bound only (`pyyaml>=6.0`), so installs are not reproducible and may resolve to different versions over time. This increases supply-chain risk and makes it difficult to verify whether a deployed version includes known security fixes or introduces a vulnerable release through future updates.

Unverifiable Dependency: pyyaml has 8 known advisory(ies) (CVE-2019-20477 (Deserialization of Untrusted Data in PyYAML); CVE-2020-1747 (Improper Input Validation in PyYAML); CVE-2020-14343 (Improper Input Validation in PyYAML) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding
PyYAML has multiple historical advisories, and because the manifest does not pin a specific version, it is impossible to confirm from this file alone whether the installed release is affected. In a skill that likely processes local configuration or report data, unsafe YAML handling combined with an affected version could lead to input-validation or deserialization-related risk if untrusted YAML is ever parsed.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pyyaml>=6.0
html2image>=2.0.0
Pillow>=10.0.0
Confidence
96% confidence
Finding
`html2image>=2.0.0` is unpinned, which allows non-deterministic installs and expands the attack surface to future package releases without review. In a rendering/HTML-to-image package, unexpected dependency changes can also affect the security posture of any browser or rendering components it invokes.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pyyaml>=6.0
html2image>=2.0.0
Pillow>=10.0.0
Confidence
97% confidence
Finding
`Pillow>=10.0.0` is not pinned to a specific release, so builds may consume different versions across environments or over time. For an image-processing library with a history of security advisories, this weakens assurance that only reviewed and fixed versions are installed.

Unverifiable Dependency: Pillow has 16 known advisory(ies) (CVE-2016-2533 (Pillow buffer overflow in ImagingPcdDecode); CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2021-27922 (Pillow Uncontrolled Resource Consumption) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
Pillow has numerous past advisories, and the unpinned requirement prevents verification that deployed environments will use a patched release. Given this skill performs visual reporting and likely handles images, using an unverified Pillow version could expose the skill to image parsing or resource-consumption issues if crafted inputs are processed.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This code file defines a helper that writes arbitrary pricing data to a caller-specified filepath, but the function has no user-facing print/log message, confirmation prompt, or warning comment describing that it will overwrite/create a file. For code files, file writes are safety-relevant operations when there is no visible disclosure at the point of action.

Intent-Code Divergence

Low
Confidence
71% confidence
Finding
The top-level documentation emphasizes strictly local behavior, but the script consults the OPENCLAW_WORKSPACE environment variable to determine storage location. Reading process environment is not the same as purely file-local processing, so the docstring overstates the implementation's constraints.

Missing User Warnings

Low
Confidence
81% confidence
Finding
The script scans ~/.openclaw and ~/.clawdbot session files and parses their contents, which may contain user activity metadata. Aside from the brief module docstring, there is no runtime warning, prompt, or explicit disclosure to the user that these local session logs will be accessed and processed.

Static analysis

No suspicious patterns detected.