Back to skill

Security audit

openclaw session viewer

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent session-viewer purpose, but it exports very sensitive session data into an unsafe local HTML/JSON artifact with documented injection and temporary-file risks.

Review before installing. Use this only for sessions you are comfortable exporting, choose a private output path, avoid sharing the generated HTML or JSON, and inspect/redact the file before opening or distributing it. The publisher should fix the HTML injection issue, avoid predictable /tmp output, use restrictive permissions, and add explicit opt-in controls for thinking, tool arguments, and tool results.

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/extract_session.py:154
Finding
Stored Script Injection in Generated Session Viewer<![CDATA[ ## Vulnerability Details **File Location**: `scripts/extract_session.py:154-155, 329, 341-342` **Vulnerability Type**: Stored script injection / unsafe HTML generation **Risk Level**: High ### Vulnerable Code ```python json_str = json.dumps(data, ensure_ascii=True) json_str = json_str.replace('</script>', '<\\/script>') ``` The serialized session data is then embedded directly into an executable script: ```python <script> var data = ''' + json_str + '''; ``` Some session metadata is also inserted into `innerHTML` without passing through the `esc()` function: ```javascript h += '<div><span class="badge badge-model">'+r.model+'</span> <span class="badge badge-tokens">'+(r.token_usage.input+r.token_usage.output)+' tok</span></div></div>'; ``` ```javascript h += '<div class="tool-item"><div class="tool-header"><span class="tool-name">'+tc.name+'</span><span style="color:#6e7681;font-size:0.75rem">'+tc.id+'</span></div>'; h += '<div class="tool-body tool-call">'+esc(JSON.stringify(tc.arguments,null,2))+'</div></div>'; ``` ### Technical Analysis The generated HTML contains session data inside an executable `<script>` element. The implementation attempts to prevent script termination by replacing the exact lowercase string `</script>`. HTML end-tag matching is ASCII case-insensitive, however. Consequently, variants such as `</ScRiPt>` are not modified by the Python replacement but are still interpreted by the browser as the end of the surrounding script element. An attacker-controlled value in a session log can therefore terminate the data script and introduce executable HTML or JavaScript. In addition, model names, tool names, and tool-call identifiers are concatenated directly into HTML strings assigned to `innerHTML`. A malicious value containing HTML with an event handler, such as an image element with an `onerror` attribute, could execute when the affected turn is rendered. Although ordinary message bodies and tool arguments are general ...[truncated 1767 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not embed untrusted JSON directly into an executable script element. 2. Store serialized data in a non-executable element, such as: ```html <script id="session-data" type="application/json"></script> ``` Populate that element using an HTML-safe serializer and parse its `textContent` with `JSON.parse()`. 3. Escape all characters that can affect HTML parsing, including at minimum `<`, `>`, and `&`. Do not rely on replacing only one lowercase closing tag. 4. Prefer constructing viewer elements with `document.createElement()` and assigning untrusted values through `textContent`. 5. If HTML-string construction remains necessary, call the escaping routine for every dynamic value, including: - `r.model` - `tc.name` - `tc.id` - `tr.tool_name` 6. Apply a restrictive Content Security Policy that disallows inline scripts and event handlers. Move viewer JavaScript to a separate static file if necessary. 7. Add regression tests containing mixed-case closing script tags, event-handler markup, quotes, angle brackets, and Unicode edge cases in every extracted field. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/extract_session.py:378
Finding
Sensitive Session Data Written to a Predictable Shared Temporary File<![CDATA[ ## Vulnerability Details **File Location**: `scripts/extract_session.py:378, 397-405` **Vulnerability Type**: Unsafe temporary-file handling and sensitive-data exposure **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument('--output', '-o', default='/tmp/session_viewer.html', help='Output HTML file') ``` ```python if args.json: output_file = args.output.replace('.html', '.json') with open(output_file, 'w') as f: json.dump(data, f, indent=2, ensure_ascii=False) print(f"✅ Saved JSON: {output_file}") else: html = generate_html(data) with open(args.output, 'w') as f: f.write(html) print(f"✅ Saved HTML: {args.output}") ``` ### Technical Analysis The default output is a fixed, predictable path in the shared `/tmp` directory. The program opens this path using the normal Python `open(..., 'w')` operation without checking whether it is a symbolic link, without using exclusive creation, and without explicitly restricting file permissions. The resulting file contains complete conversation data, potentially including hidden reasoning, tool-call arguments, command results, tokens, credentials, personal information, and other confidential material. The actual read permissions are affected by the process umask. In permissive environments, another local user may be able to read the generated artifact. The output also remains on disk after the viewer is used. Normal file opening follows symbolic links. On systems without effective temporary-directory symlink protections, or where the victim has writable access to the target, another local user could prepare `/tmp/session_viewer.html` as a symbolic link. Running the program could then truncate and overwrite the linked file with generated HTML. ### Attack Path #### Confidentiality path 1. A user runs the script without specifying `--output`. 2. The script creates or replaces `/tmp/session_viewer.html`. 3. Complete session content is written to that p ...[truncated 1404 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate output using Python's `tempfile` facilities rather than a fixed shared pathname: ```python import tempfile with tempfile.NamedTemporaryFile( mode="w", suffix=".html", prefix="openclaw-session-", delete=False, encoding="utf-8", ) as output: os.chmod(output.name, 0o600) output.write(html) ``` 2. Create generated artifacts with mode `0600` so only the invoking user can read or write them. 3. Prefer a private application data directory owned by the user instead of the shared `/tmp` directory. 4. When a user supplies an output path: - Reject symbolic links. - Use exclusive creation where replacement is not explicitly requested. - Write to a secure temporary file in the same directory and atomically rename it. 5. Warn before overwriting an existing output file. 6. Provide a cleanup option and document that the output contains sensitive session data. 7. Consider deleting temporary output automatically after the viewer exits, where practical. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/extract_session.py:69
Finding
Unredacted Export of Sensitive Reasoning and Tool Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/extract_session.py:69-148` **Vulnerability Type**: Excessive sensitive-data collection and plaintext export **Risk Level**: Medium ### Vulnerable Code ```python def extract_session(session_file): """Extract conversation data from session file.""" entries = [] with open(session_file) as f: for line in f: if line.strip(): entries.append(json.loads(line)) ``` ```python for c in msg.get("content", []): if c.get("type") == "text": response_text += c.get("text", "") elif c.get("type") == "thinking": thinking_text += c.get("thinking", "") or "" elif c.get("type") == "toolCall": current_turn["tool_calls"].append({ "id": c.get("id"), "name": c.get("name"), "arguments": c.get("arguments", {}) }) ``` ```python result_text = "" for c in msg.get("content", []): if c.get("type") == "text": result_text += c.get("text", "") details = msg.get("details", {}) current_turn["tool_results"].append({ "tool_call_id": tool_call_id, "tool_name": tool_name, "result": result_text, "details": { "status": details.get("status"), "exit_code": details.get("exitCode"), "duration_ms": details.get("durationMs"), "is_error": msg.get("isError", False) } }) ``` ### Technical Analysis The extractor copies all available assistant reasoning, tool-call arguments, and tool-result text into a consolidated output object without filtering, redaction, or field-level user selection. This behavior is consistent with the documented purpose of the session viewer and does not itself bypass filesystem permissions. Nevertheless, it creates a new plaintext artifact containing categories of data that may be substantially more sensitive than ordinary conversation messages. Tool arguments and results frequently include environment details, local paths, comma ...[truncated 1729 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Default to exporting only user and assistant message text. 2. Require explicit command-line options for sensitive categories, for example: - `--include-thinking` - `--include-tool-arguments` - `--include-tool-results` 3. Display a clear warning and request confirmation before exporting reasoning or tool data. 4. Implement configurable redaction for common secret patterns, including: - Authorization headers - API keys and access tokens - Password fields - Private keys - Session cookies - Cloud-provider credentials 5. Recursively redact sensitive keys in structured tool arguments rather than scanning only rendered strings. 6. Clearly mark generated files as sensitive and discourage sharing them without review. 7. Provide a preview or summary of included data categories before writing the output. 8. Combine these protections with restrictive output permissions and secure temporary-file creation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (4)

Context Leakage

High
Category
Data Exfiltration
Content
return sessions_dir / f"{latest['sessionId']}.jsonl"

def extract_session(session_file):
    """Extract conversation data from session file."""
    entries = []
    with open(session_file) as f:
        for line in f:
Confidence
95% confidence
Finding
The core purpose of the script is to extract and transform local OpenClaw session files into portable JSON/HTML, including user messages, assistant messages, tool invocations, tool results, usage metadata, and hidden 'thinking' content. In the context of agent session archives, this is a real context-leakage risk because such logs often contain credentials, proprietary prompts, command output, filesystem data, and other highly sensitive artifacts that become easier to exfiltrate once consolidated into a readable export.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill explicitly generates an HTML viewer containing full conversation history, raw user/assistant content, tool arguments, tool outputs, and usage statistics, but it does not warn users that highly sensitive data may be surfaced in a browsable artifact. This increases the chance of accidental disclosure through shoulder-surfing, screen sharing, saving the file in a shared location, or opening logs that contain secrets, credentials, or private reasoning/tool data.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script exports full session conversations, assistant responses, tool call arguments, tool results, and even captured 'thinking' fields into HTML or JSON files without any redaction, minimization, or explicit warning to the operator. Since session logs can contain prompts, secrets, file contents, command output, and other sensitive material, writing them to a browsable file in /tmp or another user-chosen path materially increases the risk of accidental disclosure, sharing, or local compromise.

Natural-Language Policy Violations

Low
Confidence
98% confidence
Finding
The HTML viewer hardcodes `toLocaleString('zh-CN', ...)`, which imposes a specific locale regardless of the user's preferences. This is a natural-language/locale policy concern because the file does not offer opt-in, fallback to the user's locale, or any explanation for the restriction.

Static analysis

No suspicious patterns detected.