Back to skill

Security audit

realtime-interact-overlay

Security checks for vulnerabilities and agentic risk

Overview

This skill is intended for confirmations and popups, but its password and browser modes can expose sensitive input or unsafe page scripts.

Review before installing. Use this only for low-risk confirmations unless it is fixed; do not enter passwords, payment codes, API keys, recovery phrases, or other secrets. Avoid injecting its browser JavaScript into logged-in or sensitive pages until message rendering is changed to literal text and the browser confirmation result flow is fully implemented.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/browser_modal.py:108
Finding
DOM-based cross-site scripting through unsanitized modal messages<![CDATA[ ## Vulnerability Details **File Location**: `scripts/browser_modal.py:108-133` **Vulnerability Type**: DOM-based cross-site scripting through unsafe `innerHTML` assignment **Risk Level**: High ### Vulnerable Code ```python # Escape JavaScript strings title_js = title.replace("\\", "\\\\").replace("'", "\\'").replace("\n", "\\n") message_js = message.replace("\\", "\\\\").replace("'", "\\'").replace("\n", "\\n") default_js = default_value.replace("\\", "\\\\").replace("'", "\\'") js_code = f''' (function() {{ # ... modal.querySelector('#openclaw-modal-title').textContent = '{title_js}'; modal.querySelector('#openclaw-modal-content').innerHTML = '{message_js}'.replace(/\\n/g, '<br>'); # ... }})(); ''' ``` ### Technical Analysis The modal message is derived from the command-line-controlled `message` argument. The implementation escapes selected JavaScript string characters, but it does not apply HTML escaping or sanitization before assigning the value to `innerHTML`. JavaScript string escaping and HTML sanitization address different parsing contexts. Consequently, an input containing HTML elements and event handlers can be parsed as active markup when the generated JavaScript is injected into a browser page. For example, a malicious message can contain an image element with an error handler. When the browser parses that message through `innerHTML`, the handler may execute in the security context of the active page. The title uses `textContent` and is not affected by this specific flaw. The message should use the same safe rendering approach. Exploitation requires the generated JavaScript to be executed in a browser page, as intended by the Skill's browser mode. Content Security Policy and the browser injection mechanism may constrain individual payloads, but they do not make the unsafe sink secure. ### Attack Path 1. An attacker causes untrusted content to be used as the modal `--message`, such as content copied from a ...[truncated 1502 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the unsafe `innerHTML` assignment with `textContent`: ```javascript var content = modal.querySelector('#openclaw-modal-content'); content.textContent = message; content.style.whiteSpace = 'pre-wrap'; ``` 2. Preserve line breaks with CSS such as `white-space: pre-wrap` rather than converting newlines into `<br>` tags through HTML parsing. 3. If formatted HTML is an explicit product requirement, sanitize it with a well-maintained HTML sanitizer and a strict allowlist. Disallow scripts, event-handler attributes, dangerous URL schemes, embedded frames, and active SVG or MathML content. 4. Serialize Python values into JavaScript with `json.dumps()` rather than implementing partial JavaScript-string escaping manually. This protects the JavaScript-string context, although HTML sanitization or `textContent` is still required separately. 5. Treat modal titles, messages, defaults, and options as untrusted data regardless of whether they originate from command-line arguments, websites, files, or Agent-generated content. 6. Add security tests using payloads containing event handlers, closing tags, SVG constructs, quotes, backticks, Unicode line separators, and multiline content. Verify that all payloads are displayed literally and never interpreted as markup. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/macos_dialog.py:67
Finding
Sensitive input is visibly displayed and can be emitted as plaintext<![CDATA[ ## Vulnerability Details **File Locations**: `scripts/macos_dialog.py:67-87, 153, 198-199, 205-206`; `scripts/browser_modal.py:48, 138-140`; `scripts/interact.py:15-31, 81-82` **Vulnerability Type**: Insecure handling and disclosure of password or other sensitive input **Risk Level**: Medium ### Vulnerable Code The macOS implementation accepts a hidden-input option but still creates an ordinary visible text field: ```python def create_apple_script_input(title, message, default_value="", hidden=False): # ... if hidden: # Use set password to hide input (password mode) # AppleScript hidden answer compatibility workaround script = f''' set dialogResult to display dialog "{message}" ¬ with title "{title}" ¬ with icon caution ¬ default answer "" ¬ buttons {{"取消", "确定"}} ¬ default button "确定" ¬ giving up after 60 text returned of dialogResult ''' ``` The entered value is retained in the result and JSON mode serializes the complete result: ```python parser.add_argument('--hidden', action='store_true', help='隐藏输入(用于密码等敏感信息)') # ... if "text returned:" in output: parts = output.split("text returned:") if len(parts) > 1: raw_value = parts[1].strip() result["value"] = sanitize_input(raw_value) else: result["value"] = sanitize_input(output) # ... if args.json: print(json.dumps(result, ensure_ascii=False, indent=2)) ``` The browser modal always creates a text input rather than a password input: ```html <input id="openclaw-modal-input" type="text" style=" width: 100%; padding: 12px 16px; border: 1px solid #ddd; border-radius: 8px; font-size: 14px; outline: none; transition: border-color 0.2s; "> ``` The browser implementation also returns the entered value directly: ```javascript confirmBtn.onclick = function() { var value = input.value; m ...[truncated 2984 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use a genuine concealed-input facility in the macOS implementation, such as AppleScript's supported `hidden answer` behavior. If secure masking is unavailable on a target platform, reject sensitive-input mode rather than silently falling back to a visible field. 2. Add an explicit sensitive-input mode to the browser implementation and set the field type accordingly: ```javascript input.type = sensitive ? 'password' : 'text'; input.autocomplete = sensitive ? 'current-password' : 'off'; input.spellcheck = false; ``` 3. Add a `--hidden` or `--sensitive` option to `interact.py` and propagate it through every supported backend. Do not claim password support unless all selected backends enforce masking. 4. Do not serialize secret values to standard output by default. For sensitive mode, return only a status indicator or use a narrowly scoped secure handoff mechanism to the component that immediately consumes the secret. 5. Ensure debug logging, exception handling, telemetry, and Agent transcripts never include sensitive input. Apply explicit redaction before serializing result objects. 6. Minimize secret lifetime in memory, avoid unnecessary copies between subprocesses, and discard references immediately after use. Python cannot guarantee complete memory erasure, so architectural minimization is more reliable than attempted zeroization. 7. Clearly label sensitive prompts with the requesting destination and intended operation to reduce phishing risk. Do not collect payment or account passwords unless there is a trusted and necessary consumption path. 8. Add automated tests confirming that sensitive values are masked visually, absent from stdout and stderr, excluded from JSON responses and logs, and never returned to unrelated Agent context. ]]>
Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • 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 (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The documented behavior claims trusted real-time confirmation and password/input collection via system or browser popups, but the file only describes partial/manual mechanisms and lacks a clear authenticated result-return path. That mismatch is dangerous because downstream agents or users may rely on confirmations that are not actually enforced, enabling accidental execution of sensitive actions without a reliable user-approved control point.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill explicitly instructs use of shell commands to invoke local Python scripts, but it declares no tool scope or allowed-tools boundary. In an agent environment, undeclared shell capability weakens least-privilege controls and can let a seemingly simple UI skill trigger broader local execution than reviewers or policy engines expect.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The natural-language content of the skill, including its description and operating instructions, is presented only in Chinese. Under the policy, forcing a specific language without user opt-in or a documented justification is a language/locale policy violation.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The HTML document declares `lang="zh-CN"`, and all visible UI text in the demo is presented in Chinese. This creates a natural-language locale constraint with no user opt-in, alternate language path, or documented region-specific justification.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The module docstring and later CLI/help text are written only in Chinese, which imposes a specific language on users. The policy for this audit flags language/locale restrictions unless the skill provides user opt-in or clearly documents that it is intended for a specific region or language context.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
cmd.extend(["--options", options])
    
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
        if result.returncode == 0:
            return json.loads(result.stdout)
        else:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The file’s natural-language UI and help text are written in Chinese, including the module description and later user-facing argument descriptions and dialog button labels. Because the skill does not offer any language selection or explain that it is intentionally region-specific, it imposes a locale choice on users without opt-in.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
When --hidden is requested, the code claims to provide password-style concealed entry, but the generated AppleScript still uses a normal display dialog with visible input. In this skill's context, that can expose passwords or other secrets entered during payment or confirmation flows to shoulder-surfing, screen recording, or other local observation.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""执行 AppleScript 并返回结果"""
    try:
        # 使用 osascript 执行
        process = subprocess.Popen(
            ['osascript', '-e', script],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.