Back to skill

Security audit

Loxone

Security checks for vulnerabilities and agentic risk

Overview

The skill is purpose-aligned for Loxone smart-home monitoring and control, but it allows insecure credential and token handling that could expose access to a real home controller.

Install only if you will use HTTPS/WSS wherever possible, avoid use_https=false on shared or untrusted networks, use a dedicated least-privilege Loxone account, keep config.json private, and do not share terminal or agent logs from the auth test. Treat cached structure files as sensitive home-layout data and delete or protect them when no longer needed.

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/loxone_client.py:34
Finding
HTTP Basic Credentials Can Be Transmitted Over an Unencrypted Connection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/loxone_client.py:34-53` **Vulnerability Type**: Plaintext credential transmission **Risk Level**: High The configuration documented in `SETUP.md:39-44` explicitly permits `"use_https": false`. Under that configuration, the client sends a reversible Base64-encoded username and password over plaintext HTTP. ```python self.use_https = use_https self.protocol = "https" if use_https else "http" self.base_url = f"{self.protocol}://{self.host}" self.structure = None self.rooms = {} self.controls = {} # Create auth header auth_str = f"{username}:{password}" auth_bytes = auth_str.encode('utf-8') self.auth_header = base64.b64encode(auth_bytes).decode('utf-8') def _make_request(self, endpoint: str, method: str = "GET") -> requests.Response: url = f"{self.base_url}{endpoint}" headers = { 'Authorization': f'Basic {self.auth_header}' } try: response = requests.request( method, url, headers=headers, timeout=10, verify=self.use_https ) ``` The corresponding documented configuration is: ```json { "host": "192.168.0.222", "use_https": false } ``` ### Technical Analysis HTTP Basic authentication does not encrypt credentials. The `username:password` string is only Base64-encoded, which is trivially reversible. When `use_https` is false, both the Authorization header and subsequent smart-home API traffic are exposed to network observers. The use of `verify=self.use_https` does not compensate for this issue. With an HTTP URL, no TLS connection or certificate verification occurs at all. Network communication with the configured Loxone Miniserver is necessary for the declared functionality, so the behavior is not covert exfiltration. However, transmitting reusable credentials without encryption exceeds an acceptable minimum security baseline. ### Attack Path 1. A user configures a LAN Miniserver with `"use_ ...[truncated 1213 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require HTTPS for all requests carrying credentials. 2. Reject `use_https: false` by default rather than silently permitting Basic authentication over HTTP. 3. If legacy plaintext LAN access must remain available, require an explicit high-risk override such as `allow_insecure_http: true` and display a prominent warning. 4. Prefer short-lived token authentication over repeatedly sending reusable account credentials. 5. Recommend a dedicated least-privilege Loxone account restricted to only the controls required by the Skill. 6. Update `SETUP.md` to state clearly that Base64 is not encryption and that plaintext LAN authentication exposes credentials. 7. Where a Miniserver cannot support HTTPS directly, recommend a trusted TLS-terminating reverse proxy, VPN, or another authenticated encrypted tunnel. 8. Add automated tests that reject insecure transport whenever an Authorization header is present. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/loxone_auth.py:184
Finding
Authentication Material Is Embedded in URLs and an Authenticated WebSocket URL Can Be Printed<![CDATA[ ## Vulnerability Details **File Location**: `scripts/loxone_auth.py:133-136, 184-194` **Vulnerability Type**: Authentication token exposure through URLs **Risk Level**: Medium The JWT acquisition flow places the credential-derived authentication hash in a URL path: ```python # Use getjwt instead of gettoken (v10.2+) endpoint = f"/jdev/sys/getjwt/{auth_hash}/{urllib.parse.quote(self.username)}/{permission}/{client_uuid}/{urllib.parse.quote(client_info)}" print(f"Getting JWT from: {endpoint}") print(f" (Note: Miniserver v11.2+ allows unencrypted getjwt)") response = self._make_request(endpoint) ``` The WebSocket helper embeds the issued bearer token in a query string: ```python def get_ws_url(self) -> str: """ Get WebSocket URL with token authentication Returns: WebSocket URL with token parameter """ if not self.token: raise Exception("No token available. Run authenticate() first.") scheme = "wss" if self.protocol == "https" else "ws" return f"{scheme}://{self.host}/ws/rfc6455?token={self.token}" ``` The executable authentication test subsequently prints a prefix of the token and the complete authenticated WebSocket URL: ```python token = auth.authenticate() print(f"\n✅ Authentication successful!") print(f" Token: {token[:30]}...") print(f"\n WebSocket URL:") print(f" {auth.get_ws_url()}") ``` ### Technical Analysis Bearer tokens grant access based on possession. Putting a JWT in a query string increases the number of places where it may be retained, including terminal captures, copied diagnostics, proxy logs, URL logs, monitoring systems, and support transcripts. The `main()` path prints the entire URL returned by `get_ws_url()`, including the complete JWT. Printing the first 30 characters separately is also unnecessary secret disclosure, although the complete URL is the more serious exposure. The JWT and authentication hash are sent to the configured Miniserver as part of the Loxone pro ...[truncated 1601 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never print complete bearer tokens, authenticated URLs, credential hashes, or meaningful token prefixes. 2. Redact sensitive URL components in diagnostics, for example by displaying `?token=[REDACTED]`. 3. Avoid query-string token transport where the Miniserver protocol offers a message-based or header-based authentication mechanism. 4. If a query-string token is protocol-mandated, construct it only immediately before connection and do not expose or persist the resulting URL. 5. Require `wss://` for token-authenticated WebSocket connections. 6. Request the lowest token permission level sufficient for monitoring or control, rather than defaulting all workflows to app-level permission. 7. Use short token lifetimes and implement revocation or logout when the session ends. 8. Ensure exception messages and HTTP/WebSocket diagnostics cannot include full request URLs containing authentication material. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/loxone_client.py:72
Finding
Sensitive Smart-Home Topology Is Cached Without Explicitly Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/loxone_client.py:72-76` **Vulnerability Type**: Insecure local storage of sensitive topology data **Risk Level**: Medium The client writes the complete Miniserver structure to a caller-selected cache file using default process permissions: ```python # Cache to file if requested if cache_file: with open(cache_file, 'w') as f: json.dump(self.structure, f, indent=2) print(f"Structure file cached to {cache_file}") ``` The watcher creates and reuses a project-local cache without setting explicit directory or file modes: ```python cache = Path(__file__).parent.parent / ".cache" cache.mkdir(exist_ok=True) out = cache / "LoxAPP3.json" # Re-download if older than 1 hour if out.exists() and (time.time() - out.stat().st_mtime) < 3600: return str(out) client = LoxoneClient(host, username, password, use_https=use_https) client.fetch_structure(cache_file=str(out)) ``` The main CLI also stores an `installation_map.json` file in the project root. ### Technical Analysis The Loxone structure file can contain sensitive information such as room names, control names, device types, state UUIDs, subcontrols, and automation layout. This data provides a detailed map of a user's home and connected devices. Files created with ordinary `open(..., "w")` and directories created without an explicit mode inherit the process umask. In environments with permissive umasks or shared project directories, other local users or services may be able to read the cache. This is not credential exfiltration, and the cache is functionally useful for reducing network requests. The issue is that sensitive household metadata is persisted without explicit access control or lifecycle safeguards. ### Attack Path 1. The user runs `loxone map`, `loxone rooms`, or `loxone_watch.py`. 2. The Skill downloads the complete `LoxAPP3.json` structure. 3. The structure is stored as `.cache/LoxAPP3.json` or `installation_map.json ...[truncated 981 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create cache directories with mode `0700`. 2. Create topology files atomically with mode `0600`, rather than relying on the ambient umask. 3. Check and repair permissions on existing cache files before reading or updating them. 4. Store cache data in a user-private operating-system cache directory instead of the project root. 5. Avoid following symbolic links when creating or replacing cache files. 6. Use an atomic temporary-file-and-rename sequence to prevent partial writes and reduce race conditions. 7. Provide a command or documented procedure to delete cached topology data. 8. Document that structure files contain sensitive household metadata and should not be committed, shared, or included in public diagnostics. 9. Ensure `.cache/LoxAPP3.json` and `installation_map.json` are excluded from version control and automated artifact uploads. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The code largely matches the declared smart-home Loxone control/monitoring purpose over HTTP: it authenticates, reads structure/status, organizes rooms and controls, and sends simple commands like On/Off. However, the description explicitly claims support for 'real-time WebSocket' and 'watching live events,' but the supplied code contains only HTTP request logic and no WebSocket client, subscription, or event-stream handling. File config loading and optional JSON caching are supporting details, not mismatches. The main material description-behavior gap is the missing real-time/WebSocket capability.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
Config file should be readable only by you:
```bash
chmod 600 ~/Developer/Skills/loxone/config.json
```

## Loxone Cloud DNS Setup
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill declares executable capabilities that imply filesystem and network access, but it does not define any explicit tool scope such as permissions or allowed-tools. In an agent environment, this can cause the skill to run with broader ambient authority than necessary, increasing the chance of unintended file access, network access, or misuse if the skill is invoked in the wrong context.

Intent-Code Divergence

Medium
Confidence
90% confidence
Finding
The code explicitly notes that JWT retrieval may occur unencrypted and the implementation allows `use_https=False`, which means authentication material can traverse the network over plain HTTP. In a smart-home control context, interception of authentication exchanges or issued tokens could let an attacker monitor device state or send control commands to the Miniserver.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The script prints the JWT token to stdout, which can expose bearer credentials in terminal scrollback, logs, CI output, shell history capture, or remote session recordings. Because this token appears to authorize WebSocket/API access to a smart-home controller, anyone who obtains it may be able to observe state or issue commands without needing the password.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
This code issues live control commands to devices via the Miniserver API, which can change physical or automation state. Although the docstring describes the function, there is no user-facing confirmation, warning, or visible disclosure at the point of execution that an external device state will be modified.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This code establishes a network connection and performs authentication using the supplied username and password, sending credential-derived values over the WebSocket. Although the docstring says it authenticates, there is no user-facing prompt, warning, or explicit disclosure in the code about transmitting credentials or connecting to a remote host.

Missing User Warnings

Low
Confidence
71% confidence
Finding
The code reads a configuration file and extracts username and password for authentication, but provides no visible warning or disclosure to the user that sensitive credentials are being loaded and used. In this file, the docstrings mention the parameters but do not clearly warn at the usage point about secret handling.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The script creates a .cache directory and writes LoxAPP3.json to disk via fetch_structure, which stores device structure data locally. While this supports the tool's functionality, there is no explicit user-facing warning in the CLI help or runtime output that local files will be created and reused.

Static analysis

No suspicious patterns detected.