T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/query_telegram.py:18
- Finding
- Plaintext Transmission of Sensitive Telegram Queries and API Credentials## Vulnerability Details **File Location**: `scripts/query_telegram.py:18-50`; related insecure configuration examples appear in `SKILL.md:20-21` and `skill.yaml:18-20` **Vulnerability Type**: Plaintext transmission of sensitive data **Risk Level**: Medium ### Vulnerable Code ```python # Read configuration from environment variables defined in SKILL.md base_url = os.environ.get("LIFEQUERY_BASE_URL", "http://localhost:3134/v1").rstrip("/") api_key = os.environ.get("LIFEQUERY_API_KEY", "") url = f"{base_url}/chat/completions" headers = { "Content-Type": "application/json" } # Support optional API key authentication if api_key: headers["Authorization"] = f"Bearer {api_key}" # LifeQuery uses an OpenAI-compatible API endpoint structure data = { "model": "lifequery", "messages": [ {"role": "user", "content": args.query} ], "temperature": 0.0, "stream": False } try: req = urllib.request.Request( url, data=json.dumps(data).encode("utf-8"), headers=headers, method="POST" ) with urllib.request.urlopen(req) as response: result = json.loads(response.read().decode("utf-8")) ``` The documentation explicitly permits an unencrypted remote endpoint: ```markdown - `LIFEQUERY_BASE_URL`: Base URL of your LifeQuery instance (e.g., `http://localhost:3134/v1` or `http://your-server:80/v1`) - `LIFEQUERY_API_KEY`: Optional API key if protected ``` ### Technical Analysis The base URL is entirely controlled through the `LIFEQUERY_BASE_URL` environment variable, and the script does not enforce HTTPS for non-loopback destinations. When an API key is configured, it is placed in an HTTP `Authorization: Bearer` header. The user's Telegram-history query is also placed in the request body. Although `urllib.request.urlopen` performs certificate validation for HTTPS URLs, it provides no transport confidentiality or server authentication when the configured URL uses HTTP. The documented `http://y ...[truncated 1831 chars]
- Remediation
- ## Remediation Suggestions 1. Require `https://` for every non-loopback LifeQuery endpoint. 2. Permit plaintext HTTP only for explicitly recognized loopback addresses such as `localhost`, `127.0.0.1`, and `::1`. 3. Reject startup or request execution when an API key would be transmitted over HTTP. 4. Replace the documented remote example with an HTTPS URL, such as `https://your-server.example/v1`. 5. Parse and validate the URL with `urllib.parse.urlparse` rather than relying on string-prefix checks. Reject missing schemes, embedded credentials, and unsupported schemes. 6. Preserve normal TLS certificate and hostname verification. Do not introduce an unverified SSL context as a compatibility workaround. 7. Consider an explicit opt-in override only for controlled development environments, accompanied by a prominent warning and disabled by default. 8. Use narrowly scoped, short-lived API credentials where supported, and rotate any credential previously sent through a remote HTTP endpoint. A suitable validation policy is: ```python from urllib.parse import urlparse import ipaddress parsed = urlparse(base_url) host = parsed.hostname is_loopback = host == "localhost" if host and not is_loopback: try: is_loopback = ipaddress.ip_address(host).is_loopback except ValueError: pass if parsed.scheme != "https" and not (parsed.scheme == "http" and is_loopback): raise ValueError( "LIFEQUERY_BASE_URL must use HTTPS unless it targets a loopback address." ) if api_key and parsed.scheme != "https": raise ValueError("Refusing to send LIFEQUERY_API_KEY over plaintext HTTP.") ```
