Back to skill

Security audit

desktop-control

Security checks for vulnerabilities and agentic risk

Overview

This skill is a powerful desktop automation tool whose core purpose is plausible, but it understates network, logging, recording, and local-control risks users should review before installing.

Install only on a trusted Windows machine where you are comfortable granting an agent full interactive desktop control. Treat LLM script generation as network-enabled despite the local-only claims, avoid putting secrets in prompts/context or macros, prefer confirm/review modes before running generated scripts, and inspect logs/cache under the DesktopControl directories if sensitive data may have been used.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
daemon/script_gen/llm_client.py:80
Finding
Undisclosed transmission of automation prompts and context to configurable external endpoints<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:27-31`, `daemon/script_gen/prompts.py:151-160`, `daemon/script_gen/llm_client.py:29-35`, `daemon/script_gen/llm_client.py:80-104` **Vulnerability Type**: Misleading network-egress claim and insufficient protection of externally transmitted data **Risk Level**: High ### Vulnerable Code The Skill documentation claims that all operations remain local: ```markdown Windows desktop daemon providing keyboard/mouse control, screenshots, window management, OCR, and UI Automation through a named pipe. All operations execute locally, with zero network egress. ``` The prompt builder includes the complete user prompt and every value supplied through the context object: ```python def build_user_prompt(prompt: str, context: dict = None) -> str: """Build a user prompt from the natural language description and optional context.""" lines = [f"Generate a desktop automation script for: {prompt}"] if context: lines.append("\nAdditional context:") for key, value in context.items(): lines.append(f" - {key}: {value}") lines.append("\nOutput ONLY valid JSON matching the schema above.") return "\n".join(lines) ``` The resulting content is sent to a configurable endpoint: ```python LLM_PROVIDER = os.environ.get("LLM_PROVIDER", "").strip().lower() LLM_BASE_URL = os.environ.get("LLM_BASE_URL", "").strip().rstrip("/") LLM_API_KEY = os.environ.get("LLM_API_KEY", "").strip() LLM_MODEL = os.environ.get("LLM_MODEL", "").strip() # Fallback: if only LLM_API_KEY is set, try OpenAI format _FALLBACK_URL = "https://api.openai.com/v1" _FALLBACK_MODEL = "gpt-4o-mini" ``` ```python def _chat_completion(system_prompt: str, user_prompt: str, temperature: float = 0.3, max_tokens: int = 4096) -> str: """Send a chat completion request to an OpenAI-compatible API. Returns the assistant's response text. Raises RuntimeError on failure. """ import ...[truncated 2849 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Correct `SKILL.md` and `README.md` to explicitly disclose optional external LLM communication, transmitted fields, destinations, and retention implications. 2. Require explicit opt-in before enabling network-backed generation. 3. Require per-request confirmation that shows the destination and a preview of the exact data being transmitted. 4. Enforce HTTPS for non-loopback endpoints and reject URLs containing embedded credentials. 5. Maintain an explicit provider allowlist or require administrator approval for custom endpoints. 6. Apply recursive, allowlist-based filtering to context data. Exclude credentials, clipboard contents, tokens, passwords, and raw screen text by default. 7. Disable automatic redirects or revalidate every redirect destination. 8. Separate local-model mode from remote-provider mode in both configuration and user-visible behavior. 9. Avoid returning remote response bodies in errors where they could introduce additional sensitive information into logs. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
daemon/handlers/script_gen_handler.py:145
Finding
Untrusted LLM-generated desktop automation is executed without confirmation by default<![CDATA[ ## Vulnerability Details **File Location**: `daemon/handlers/script_gen_handler.py:145-175`, `daemon/script_gen/generator.py:279-319`, `daemon/script_gen/generator.py:354-367`, `daemon/script_engine/engine.py:64-105`, `daemon/script_engine/engine.py:215-254` **Vulnerability Type**: Remote generation and automatic execution of privileged desktop actions **Risk Level**: High ### Vulnerable Code The generate-and-run handler defaults to immediate execution: ```python def handle_script_generate_and_run(params): """Generate a script from natural language and execute it. Params: prompt: Natural language description (required). confirm: If True, only generate and return script (no execution). Default: False (generate and execute). context: Optional dict with extra context. """ prompt = params.get("prompt") if not prompt: raise ValueError( "Missing required parameter 'prompt' for script_generate_and_run. " "Provide a natural language description of what you want to automate." ) confirm = params.get("confirm", False) context = params.get("context", {}) if confirm: # Generate only (safe mode) return handle_script_generate(params) # Generate and execute result = _gen_run(prompt, context) ``` The remote response is parsed, structurally validated, and submitted for execution: ```python try: user_prompt = build_user_prompt(prompt, context) raw = llm_generate(SYSTEM_PROMPT, user_prompt) raw_json = extract_json(raw) script = json.loads(raw_json) except json.JSONDecodeError as e: return { "valid": False, "script": None, "error": f"LLM returned invalid JSON: {e}", "raw_response": raw, } except RuntimeError as e: return { "valid": False, "script": None, "error": str(e), } # Validate the generated script validation = validate_script(script) ...[truncated 4321 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Change the default to `confirm=True`; generated scripts must never execute merely because the caller omitted a field. 2. Separate generation and execution into two distinct APIs. 3. Require an explicit approval token tied to a cryptographic hash of the exact reviewed script. 4. Invalidate approval if any action, parameter, order, or nested control-flow element changes. 5. Classify actions by risk. Require additional confirmation for keyboard input, hotkeys, window closure, file drops, screenshots, OCR, and UIA interactions. 6. Detect and reject UI-mediated command-launch patterns unless the user explicitly approves them. 7. Constrain generated scripts to the foreground application and target window authorized by the user. 8. Apply parameter-level validation, including path policies, coordinate bounds, text-length limits, loop limits, and prohibited key combinations. 9. Display a human-readable action plan before execution. 10. Prefer deterministic local templates for common operations and reserve remote generation for reviewed plan creation. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
daemon/server.py:227
Finding
Named-pipe access control fails open and requests have no independent authentication<![CDATA[ ## Vulnerability Details **File Location**: `daemon/server.py:227-287`, `daemon/server.py:297-310`, `daemon/main.py:18-20`, `daemon/main.py:56-59`, `daemon/server.py:73-160` **Vulnerability Type**: Fail-open local IPC authorization **Risk Level**: High ### Vulnerable Code The daemon attempts to create a strict DACL but falls back to default security attributes after any setup failure: ```python def _create_secure_security_attributes(self): """Build SECURITY_ATTRIBUTES with a strict DACL. Only allows: - Current user (SID from token): FULL_CONTROL - SYSTEM: DENY (prevents SYSTEM-level processes on the same box from connecting to our pipe) """ try: # Current user SID token = win32security.OpenProcessToken( win32api.GetCurrentProcess(), win32con.TOKEN_QUERY, ) user_sid = win32security.GetTokenInformation( token, win32security.TokenUser )[0] # SECURITY_DESCRIPTOR sd = win32security.SECURITY_DESCRIPTOR() sd.Initialize() # DACL: allow current user, deny SYSTEM acl = win32security.ACL() acl.AddAccessAllowedAce( win32security.ACL_REVISION, win32con.GENERIC_ALL, user_sid, ) try: system_sid, _, _ = win32security.LookupAccountName(None, "SYSTEM") acl.AddAccessDeniedAce( win32security.ACL_REVISION, win32con.GENERIC_ALL, system_sid, ) except Exception as sys_err: import sys as _sys _sys.stderr.write( f"[WARNING] DesktopControl: SYSTEM DENY ACE addition failed " f"(non-critical, allow-only ACE still set). Error: {sys_err}\n" ) sd.SetSecurityDescriptorDacl(1, acl, 0) sa = pywintypes.SECURITY_ATTRIBUTES() sa.SECURITY_DESCRIPTOR = sd sa.bInheritHandle = False ...[truncated 3737 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Fail closed: terminate daemon startup if the intended DACL cannot be created or verified. 2. After creating the pipe, query and verify its effective security descriptor before accepting clients. 3. Apply explicit owner-only ACLs to the pipe discovery file and PID file. 4. Store discovery information in a per-user protected directory rather than relying only on the temporary directory. 5. Generate a cryptographically random capability token and store it with owner-only permissions. 6. Require every request to authenticate using that capability and use constant-time token comparison. 7. Add replay resistance through request nonces or a short-lived authenticated session. 8. Where practical, inspect the named-pipe client process token and reject clients outside the expected user, session, or integrity level. 9. Introduce per-method authorization so read-sensitive and destructive actions require stronger approval. 10. Add automated tests that deliberately force DACL construction failures and verify that no pipe server starts. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
daemon/utils/lifecycle.py:124
Finding
Shallow audit-log redaction persists sensitive request data in plaintext<![CDATA[ ## Vulnerability Details **File Location**: `daemon/utils/lifecycle.py:124-153`, `daemon/server.py:157-165` **Vulnerability Type**: Plaintext sensitive-data exposure through incomplete logging redaction **Risk Level**: Medium ### Vulnerable Code Only three top-level parameter names receive special treatment: ```python def log_action(pid, method, params, success): """Write a structured audit log entry (no sensitive data). Records: timestamp, PID, method name, sanitised params (text length only), success status. Written as one JSON line per entry. """ try: _ensure_log_dir() import datetime safe_params = {} for k, v in params.items(): if k in ("text",): safe_params[k] = f"<{len(str(v))} chars>" elif k in ("password", "secret"): safe_params[k] = "<redacted>" else: safe_params[k] = v entry = { "time": datetime.datetime.now().isoformat(), "pid": pid, "method": method, "params": safe_params, "success": success, } with open(LOG_FILE, "a", encoding="utf-8") as f: f.write(json.dumps(entry, ensure_ascii=False) + "\n") except Exception: pass # Logging failure should never crash the daemon ``` Every dispatched request is passed to this logger: ```python try: result = handler(params) lifecycle.log_action(os.getpid(), method, params, True) return _respond(True, data=result, req_id=req_id) except Exception as e: lifecycle.log_action(os.getpid(), method, params, False) tb = traceback.format_exc() return _respond(False, error={"code": "HANDLER_ERROR", "message": str(e)}, req_id=req_id) ``` ### Technical Analysis The claim that the audit entry contains “no sensitive data” is not supported by the implementation. Redaction is based only on exact top-level key names. Sensitive values remain unredacte ...[truncated 1677 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace key-name blacklisting with an explicit allowlist of non-sensitive metadata. 2. Log method names, timestamps, success status, duration, and coarse parameter counts rather than request bodies. 3. If parameter logging is required, implement recursive traversal across dictionaries and arrays. 4. Redact keys case-insensitively and cover common names such as `prompt`, `context`, `input`, `token`, `api_key`, `authorization`, `clipboard`, and `script`. 5. Do not log nested keyboard text, OCR results, screenshots, clipboard data, or generated prompts. 6. Apply owner-only ACLs to the log directory and files. 7. Implement size-based rotation, short retention periods, and secure deletion where appropriate. 8. Provide a configuration option to disable payload logging entirely. 9. Add tests with deeply nested sensitive fields to verify that plaintext values never appear in log output. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Automatically installed dependencies are unpinned and lack integrity verification<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-6`, `SKILL.md:11-16`, `SKILL.md:29-31` **Vulnerability Type**: Unbounded dependency resolution during automatic installation **Risk Level**: Medium ### Vulnerable Code Every dependency uses a lower-bound constraint without an upper bound, exact version, or package hash: ```text pywinauto>=0.6.8 mss>=9.0.1 psutil>=5.9.0 pywin32>=306 pyperclip>=1.8.2 requests>=2.28.0 # Optional: only needed for LLM-based script generation ``` The Skill metadata and instructions indicate that these dependencies are installed automatically: ```yaml requires: bins: - python - pip install: - kind: pip package: -r requirements.txt bins: [python] ``` ```markdown - On first use, `pip install -r {baseDir}/requirements.txt` runs automatically. ``` ### Technical Analysis The use of `>=` permits pip to resolve any future release satisfying the minimum version. The installation does not use a reviewed lock file, hashes, a trusted private mirror, or `--require-hashes`. This makes the installed code differ over time even when the Skill itself has not changed. A compromised future release of any dependency would execute in the daemon’s process and inherit its desktop-control capabilities. The issue is especially significant because the dependencies include Windows automation and process-management packages imported by a long-running daemon. `requests` is described as optional but is installed unconditionally, increasing the default dependency surface. No evidence of a currently malicious package was found; this finding concerns unsafe supply-chain and reproducibility controls. ### Attack Path 1. A dependency account, release pipeline, or package-index delivery path is compromised, or a future incompatible release is published. 2. A user invokes the Skill on a new machine or in a fresh environment. 3. Automatic `pip install -r requirements.txt` resolves the newest allowed versions. 4. pip ...[truncated 797 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every dependency to an exact, reviewed version. 2. Generate and commit cryptographic hashes for all distributions. 3. Install with `pip install --require-hashes -r requirements.txt`. 4. Use a lock-generation workflow that records transitive dependencies as well as direct dependencies. 5. Configure a trusted package index or reviewed internal mirror. 6. Separate optional LLM dependencies into an extras file and install them only after explicit opt-in. 7. Add automated dependency vulnerability and provenance scanning to release workflows. 8. Review dependency updates before changing the lock file rather than resolving arbitrary versions at runtime. 9. Consider shipping a signed, reproducible environment or wheel bundle for offline installation. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • 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 (299)

Tainted flow: 'url' from os.environ.get (line 90, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
}

    try:
        resp = requests.post(url, headers=headers, json=payload, timeout=60)
        resp.raise_for_status()
        data = resp.json()
    except requests.exceptions.Timeout:
Confidence
93% confidence
Finding
The request target is derived from environment-controlled configuration and the code sends full prompts plus the Authorization bearer token to that endpoint without any allowlist, scheme restriction, or trust validation. In a desktop-control skill, this creates a real exfiltration and SSRF-style risk because sensitive user instructions or generated scripts could be transmitted to an attacker-controlled service if the environment is tampered with.

Context-Inappropriate Capability

Critical
Confidence
99% confidence
Finding
Arbitrary script execution is a highly dangerous capability in a desktop-control skill because it can run attacker-influenced code on the host, bypassing the apparent narrow UI automation scope. In the context of a Windows desktop control tool, this can enable full system compromise, data theft, persistence, lateral movement, or disabling security controls if an LLM or user prompt is manipulated into invoking it.

Intent-Code Divergence

High
Confidence
97% confidence
Finding
The README claims keyboard input is not recorded or exfiltrated, but later advertises macro recording of keyboard actions. For a tool that can capture screenshots and inject input, this contradiction is especially dangerous because users may expose passwords, messages, or other sensitive data believing keystrokes are never captured.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Local file read/write combined with source-code patching is a serious integrity risk because it permits modification of executable logic and local artifacts, not merely desktop interaction. This goes well beyond the trust users would reasonably grant to a screenshot and input-control skill.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
daemon/script_engine/engine.py:52