Back to skill

Security audit

desktop-automation-100per100-local

Security checks for vulnerabilities and agentic risk

Overview

This skill is not clearly malicious, but it needs Review because it can control the desktop, record keystrokes, capture screen and clipboard data, and its safety controls are incomplete.

Install only if you intentionally want a local agent to have broad desktop-control authority. Do not record or replay workflows involving passwords, payment data, private messages, admin prompts, terminals, or sensitive business apps; inspect macro JSON before playback; keep macros, screenshots, reports, and logs in a protected location; use a least-privileged account; and prefer pinned dependency versions in an isolated environment.

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

T09 · Insecure Skill Coding Practices

Error
Location
lib/automation.py:552
Finding
Safe Mode Can Be Bypassed Through Macro Playback<![CDATA[ ## Vulnerability Details **File Location**: `lib/automation.py:552-556`, `lib/safety.py:44-73`, `scripts/play_macro.py:82-154`, `scripts/play_macro.py:271-292` **Vulnerability Type**: Safety-control bypass through an unvalidated subprocess execution path **Risk Level**: High ### Vulnerable Code The main dispatcher validates only the top-level `play_macro` action and then launches a separate process: ```python def play_macro(macro_path, speed=1.0): script_dir = os.path.dirname(os.path.abspath(__file__)) player_script = os.path.join(script_dir, '..', 'scripts', 'play_macro.py') if not os.path.exists(player_script): return {"status": "error", "message": f"Player script not found: {player_script}"} if not os.path.exists(macro_path): return {"status": "error", "message": f"Macro file not found: {macro_path}"} try: subprocess.run([sys.executable, player_script, macro_path, str(speed)], check=True) return {"status": "ok"} except subprocess.CalledProcessError as e: return {"status": "error", "message": str(e)} except Exception as e: return {"status": "error", "message": str(e)} ``` The safety layer treats only action names containing selected risky-action strings as risky: ```python def validate_action(self, action: str, params: Dict[str, Any]) -> Dict[str, Any]: for param_key, values in params.items(): if param_key in self.DANGEROUS_PATTERNS: if isinstance(values, str): for pattern in self.DANGEROUS_PATTERNS[param_key]: if pattern.lower() in values.lower(): msg = f"Dangerous pattern '{pattern}' detected in param '{param_key}': {values}" logger.warning(msg) if self.safe_mode: return { 'allowed': False, 'reason': f"Security: {msg}", ...[truncated 3543 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate every macro event immediately before execution using the same centralized `SafetyInterlock` used by the main dispatcher. 2. Classify `play_macro` and `play_macro_with_subroutines` as risky actions requiring explicit user authorization. 3. Parse and validate the entire macro before launching playback: - Enforce an explicit action allowlist. - Validate required fields and parameter types. - Apply coordinate, duration, interval, timeout, and event-count limits. - Reject nested or unknown actions. 4. Propagate `dry_run` to the child process rather than removing it at the wrapper boundary. 5. Prefer in-process playback through a single guarded action manager instead of a separate script with duplicated execution logic. 6. Require confirmation for command-submission sequences, terminal activation, Enter presses, destructive shortcuts, and macros from untrusted locations. 7. Consider signing trusted macros or recording and verifying a content hash before execution. 8. Ensure sub-macro events receive identical validation and cannot bypass directory restrictions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
lib/safety.py:44
Finding
Sensitive Text Is Persisted in Plaintext Logs<![CDATA[ ## Vulnerability Details **File Location**: `lib/safety.py:44-58`, `lib/actions.py:76-90` **Vulnerability Type**: Plaintext sensitive-data exposure through diagnostic logging **Risk Level**: Medium ### Vulnerable Code The safety validator embeds the complete parameter value in a warning message: ```python for param_key, values in params.items(): if param_key in self.DANGEROUS_PATTERNS: if isinstance(values, str): for pattern in self.DANGEROUS_PATTERNS[param_key]: if pattern.lower() in values.lower(): msg = f"Dangerous pattern '{pattern}' detected in param '{param_key}': {values}" logger.warning(msg) if self.safe_mode: return { 'allowed': False, 'reason': f"Security: {msg}", 'params': params } ``` The alternate action manager also logs complete typed strings: ```python def type(self, text: str, interval: float = 0.05, dry_run: bool = False, **kwargs) -> Dict[str, Any]: """Type text.""" params = {"text": text} if not self._check_safe("type", params): return {"status": "blocked", "reason": "Safe mode active"} try: if dry_run: logger.info("[DRY RUN] type '%s' with interval %.2f", text, interval) return {"status": "ok", "dry_run": True, "text": text} with self.lock: pyautogui.typewrite(text, interval=interval) logger.info("Typed: %s", text) ``` ### Technical Analysis The configured dangerous patterns explicitly include terms such as `password`, `secret`, and `token`. If submitted text contains one of these terms, the safety validator writes the entire value to the warning log before rejecting it. The same value is also included in the returned error reason. The alternate action implementation logs complete text both during dry-run a ...[truncated 1587 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove raw parameter values from all safety, typing, clipboard, and macro logs. 2. Log only non-sensitive metadata, such as: - Action name. - Text length. - Whether validation passed. - A request or correlation identifier. 3. Implement a centralized structured-logging redaction layer that masks fields such as `text`, `password`, `secret`, `token`, clipboard data, macro parameters, and decrypted events. 4. Do not return complete rejected values in API error messages. 5. Disable raw-text logging in dry-run mode; dry-run data can be just as sensitive as executed data. 6. Set restrictive permissions on log directories and files, such as owner-only access where supported. 7. Define retention and secure-deletion policies for automation logs. 8. Add tests that submit representative secrets and assert that neither logs nor returned errors contain those values. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
lib/advanced_automation.py:501
Finding
Macro Report Generation Allows Stored HTML and Script Injection<![CDATA[ ## Vulnerability Details **File Location**: `lib/advanced_automation.py:501-541` **Vulnerability Type**: Stored HTML injection caused by unescaped report fields **Risk Level**: Medium ### Vulnerable Code Dynamic values are interpolated directly into an HTML document: ```python html_path = os.path.join(report_dir, report_base + '.html') with open(html_path, 'w', encoding='utf-8') as f: f.write(f"""<!DOCTYPE html> <html> <head> <title>Macro Report — {os.path.basename(macro_path)}</title> <style> body {{ font-family: sans-serif; margin: 20px; }} table {{ border-collapse: collapse; width: 100%; }} th, td {{ border: 1px solid #ccc; padding: 8px; text-align: left; }} th {{ background: #f0f0f0; }} .success {{ color: green; }} .error {{ color: red; }} </style> </head> <body> <h1>Macro Execution Report</h1> <p><strong>Macro:</strong> {macro_path}</p> <p><strong>Generated:</strong> {timestamp}</p> <h2>Summary</h2> <ul> <li>Total actions: {len(execution_log.get('actions', []))}</li> <li>Status: {execution_log.get('status', 'unknown')}</li> <li>Duration: {execution_log.get('elapsed', 0):.2f}s</li> </ul> <h2>Actions Log</h2> <table> <tr><th>#</th><th>Action</th><th>Params</th><th>Result</th></tr> """) for i, act in enumerate(execution_log.get('actions', [])): result_class = 'success' if act.get('result', {}).get('status') == 'ok' else 'error' f.write(f"<tr><td>{i+1}</td><td>{act.get('action')}</td><td>{act.get('params')}</td><td class='{result_class}'>{act.get('result')}</td></tr>\n") f.write(""" </table> </body> </html> """) ``` ### Technical Analysis The macro path and execution-log fields are inserted directly into HTML without context-aware escaping. Values containing HTML elements, event-handler attributes, or script markup are therefore interpreted as document structure rather than displayed as text. Because the generated file is persistent, this is a stored injection vulnerability. The payload executes when a user opens the ...[truncated 1265 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape every dynamic HTML value with `html.escape(..., quote=True)` before interpolation. 2. Prefer a maintained template engine with automatic escaping enabled. 3. Serialize dictionaries and lists to escaped JSON or render them inside `<pre>` elements as text. 4. Apply escaping to the macro basename, full path, status, action name, parameters, and results. 5. Add a restrictive Content Security Policy, for example one that blocks scripts and external resources. 6. Avoid inline JavaScript and inline event handlers entirely. 7. Consider generating a plain-text, JSON-only, or Markdown report when active HTML is unnecessary. 8. Add regression tests containing `<script>`, `<img onerror=...>`, quotes, and malformed tags, and verify that the generated report displays them literally. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unbounded Dependency Constraints Allow Unreviewed Future Packages<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-6`, `SKILL.md:63-66`, `skill.yaml:10-27` **Vulnerability Type**: Non-reproducible and insufficiently constrained dependency installation **Risk Level**: Medium ### Vulnerable Code The installation file accepts any future version greater than or equal to the stated minimum: ```text pyautogui>=0.9.53 pygetwindow>=0.0.9 Pillow>=8.0.0 opencv-python>=4.5.0 pytesseract>=0.3.10 pyperclip>=1.8.2 ``` The documented installation command resolves those mutable constraints from the configured package index: ```bash pip install -r requirements.txt ``` The dependency declarations are also inconsistent. `skill.yaml` declares exact versions and additional runtime packages that are absent from `requirements.txt`: ```yaml requirements: - python>=3.10 - pyautogui==0.9.54 - pygetwindow==0.0.9 - Pillow==10.4.0 - opencv-python==4.10.0.84 - pyperclip==1.9.0 - pytesseract==0.3.10 - pynput==1.7.6 - openpyxl==3.1.5 - pandas==2.2.3 - cryptography==44.0.1 - mss==9.0.1 - numpy==1.26.4 ``` ### Technical Analysis Lower-bound-only constraints make installation results dependent on the time, package index, resolver state, and transitive dependency graph at installation. A future release is accepted automatically without being reviewed as part of this Skill. Python packages can execute code during installation and are later imported with the privileges of the Skill process. Consequently, compromise of a dependency release or package-index resolution can introduce code not present during the audit. The mismatch between `requirements.txt` and `skill.yaml` further undermines reproducibility. Users following the documented `pip install -r requirements.txt` process may receive a materially different environment from the one represented by the metadata, and some advanced features depend on undeclared packages in that file. No currently listed package was proven malicious during this static a ...[truncated 1050 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace lower-bound requirements with reviewed exact versions. 2. Generate a lock file that includes the complete transitive dependency graph. 3. Require package hashes, such as through `pip install --require-hashes`, to prevent silent artifact substitution. 4. Reconcile `requirements.txt`, `skill.yaml`, and dependency documentation so all installation paths produce the same environment. 5. Separate mandatory and optional dependencies into explicit, version-locked groups. 6. Use a trusted package index and disable unneeded alternate indexes to reduce dependency-confusion exposure. 7. Add automated vulnerability and integrity scanning to the release process. 8. Review and deliberately update locked versions rather than automatically accepting future releases. 9. Install dependencies in an isolated virtual environment under a non-privileged account. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (69)

Hidden Instructions

High
Category
Prompt Injection
Content
# Desktop Automation Skill for OpenClaw

**⚠️  PRIVACY WARNING**: The macro recorder captures **ALL** keyboard events (including passwords, credit card numbers, private messages) and window titles. **Never record while entering credentials.** Only use for non-sensitive workflows. Store recorded macros securely.
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

YARA rule 'keylogger_indicators': Keylogger functionality in scripts or source code [malware]

High
Category
YARA Match
Content
words, credit card numbers, and other sensitive data. Use with extreme caution.
Only record macros for non-sensitive workflows. Never record while entering credentials.
"""
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, ttk
import json, os, time, threading, logging, sys
from datetime import datetime
from pynput import mouse, keyboard
from pynput.mouse import Button
from pynput.keyboard import Key

# ============ CONFIGURATION LOGGING ============
logging.basicConfig(
    level=logging.INFO,
    format='[%(levelname)s] %(asctime)s — %(message)s',
    handlers=[logging.StreamHandler(sys.stderr)]
)
logger = logging.getLogger(__name__)

# ============ HANDLER TKINTER POUR LOGS ============
class TkinterLogHandler(logging.Handler):
    """Handler qui écrit dans un widget ScrolledText de Tkinter"""
    def __init__(self, text_widget, max_lines=100):
        super().__init__()
        self.text_widget = text_widget
        self.max_lines = max_lines
Confidence
99% confidence
Finding
This script implements global keyboard capture via pynput and persists captured keystrokes to disk, which is keylogger-like behavior even if presented as a macro recorder. In this skill context, it is especially dangerous because it records all keystrokes system-wide, including passwords and other secrets, and also correlates them with window titles, increasing the sensitivity of the collected data.

Memory Manipulation

High
Category
Memory Poisoning
Content
except Exception as e:
            raise IOError(f"Dossier de sortie inaccessible ({output_dir}) : {e}")

        # Reset state
        self.output_path = output_path
        self.recording = True
        self.events = []
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def test_dangerous_pattern_detection(self):
        """Test dangerous pattern detection."""
        self.assertTrue(self.manager.contains_dangerous_pattern("rm /etc/"))
        self.assertFalse(self.manager.contains_dangerous_pattern("echo hello"))
    
    def test_check_action_safety(self):
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The documentation includes an example that types email and password values into detected fields, despite earlier privacy warnings about keyboard capture and sensitive input. Providing copyable credential-automation examples normalizes unsafe handling of secrets and can lead users or agents to hardcode passwords into macros, logs, or recorded workflows.

Session Persistence

Medium
Category
Rogue Agent
Content
}
```

### excel_write
Write an array of objects (or array of arrays) to an Excel file.

```javascript
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
90% confidence
Finding
The README asserts that safe mode blocks risky actions like typing, clicking, and key presses by default, while elsewhere documenting those same actions as normal capabilities and showing direct usage examples. This can mislead users or downstream agents into assuming harmful actions are prevented when they may still be invoked or when enforcement details are unclear, increasing the chance of unintended desktop interaction or credential entry.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The documented OCR-related actions hard-code `lang="fra"` as the default for multiple commands. This imposes a specific locale/language behavior on users without presenting a choice or documenting a region-specific reason, which matches the language/locale policy violation criteria.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
export AUTOMATION_LOG_DIR=~/my_logs

# Disable safe mode globally (NOT recommended)
export AUTOMATION_SAFE_MODE=false
```

---
Confidence
92% confidence
Finding
This skill performs powerful desktop automation including keyboard capture, input injection, screenshots, clipboard access, OCR, and macro replay. Documenting a global environment variable that disables safe mode materially lowers protections and can enable destructive or stealthy automation, especially because the skill already warns it can capture sensitive input and interact with arbitrary desktop state.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The module advertises safety, but its protections are weak and optional: safe mode can be disabled at runtime, and the remaining checks only look for a few dangerous substrings in string parameters. The class still exposes broad desktop-control primitives such as clicking, typing, dragging, window activation, screenshot capture, and clipboard interaction, which can be abused to manipulate sensitive applications or exfiltrate data even when the simplistic checks do not trigger.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The screenshot function captures the entire screen and writes it to disk without any user-facing warning, confirmation, or scoping controls. In a desktop automation context, this can expose secrets visible on screen such as passwords, tokens, messages, financial data, or internal documents, and storing them on disk increases persistence and exfiltration risk.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The clipboard paste function reads clipboard contents and automatically types them into the active application without warning or target validation. Because clipboard data often contains credentials, API keys, personal data, or other secrets, this behavior can leak sensitive information into unintended windows, chats, terminals, or remote sessions with a single invocation.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The function hard-codes a default OCR language of 'fra', which imposes a specific locale choice unless the caller overrides it. The policy requires avoiding forced language or locale behavior without explicit user choice or documented justification.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
This code captures the full screen and sends the image into OCR processing, which can include sensitive user or system information visible on screen. The function has no user-facing print/log warning, confirmation step, or explicit disclosure in comments/docstrings about handling potentially sensitive screen contents.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
When evaluating text conditions, the monitor falls back to 'fra' if no language is provided, enforcing a locale choice implicitly. This is a natural-language policy issue because the user is not offered a language choice and no regional justification is documented.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The macro player loads action definitions from a file and executes them dynamically via globals() with no trust boundary, allowlist enforcement, confirmation step, or sandboxing. In a desktop automation context, a crafted macro can trigger arbitrary exported automation primitives such as typing, clicking, clipboard operations, or window manipulation, enabling harmful or deceptive system-wide actions.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The stop manager claims to provide a global emergency hotkey, but the listener never sets stop_requested and does not actually implement parsing or detection of the configured stop_hotkey. In an automation skill that can click, type, and replay macros globally, a non-functional stop mechanism can leave users unable to interrupt runaway actions, causing unintended system interaction or destructive automated input.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
set_safe_mode = safety.set_safe_mode
    else:
        get_safety = None
        set_safe_mode = None
        logging.warning("safety.py not available — security features disabled")

logger = logging.getLogger(__name__)
Confidence
98% confidence
Finding
If safety.py is absent, the library silently disables security controls and continues exposing powerful desktop automation actions such as clicks, typing, screenshots, clipboard access, OCR, and macro execution. In an agent skill context, failing open on missing safety enforcement materially increases the risk of unauthorized UI manipulation and sensitive data capture, because the skill remains fully operational without its intended guardrails.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The function captures the current screen and writes the image to disk, which can expose sensitive on-screen information and create a persistent artifact. Although the file has logging setup elsewhere, this operation itself provides no confirmation prompt, print/log disclosure, or inline warning describing that screen contents will be saved.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The function defaults OCR processing to 'fra', which imposes a specific language/locale choice on users rather than letting them choose or documenting a region-specific requirement. This is a natural-language locale policy concern because it silently biases behavior toward French recognition.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The function screenshots all or part of the display and extracts structured text from it, which may process sensitive personal, financial, or credential-related information visible on screen. While there is a French docstring describing OCR behavior, it does not clearly warn users about the privacy implications of collecting screen contents.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
Using a fixed 'fra+eng' OCR language set enforces locale assumptions that may not match the user's environment or preferences. The file does not offer opt-in selection or explain a compliance or regional reason for restricting recognition to these languages.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This function starts an external recorder script via subprocess, which can observe and capture user input events and create recorded automation artifacts. There is no confirmation prompt, visible log/print notice, or explanatory docstring/comment warning the user that recording will begin.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if output_path:
        args.append(output_path)
    try:
        subprocess.Popen(args)
        return {"status": "started", "message": "Macro recorder GUI launched"}
    except Exception as e:
        return {"status": "error", "message": str(e)}
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This function invokes a macro player subprocess that may generate clicks, keystrokes, or other desktop actions affecting user applications and data. The code contains no inline warning, confirmation, or user-facing disclosure that a prerecorded macro will be executed on the user's desktop.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.dynamic_code_execution

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
skill.js:14

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
lib/automation.py:22