Back to skill

Security audit

Telegram History via LifeQuery

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it can query an entire private Telegram history and supports sending sensitive queries and bearer tokens to unvalidated or plaintext remote endpoints.

Review before installing. Use this only with a LifeQuery instance you control, prefer localhost or HTTPS, avoid remote HTTP endpoints, and do not set LIFEQUERY_API_KEY where it could be sent over plaintext. Treat each query as disclosure of private Telegram context and ask the user to narrow the chat, topic, or time range before use.

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

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.") ```
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (4)

Tainted flow: 'req' from os.environ.get (line 43, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST"
        )
        
        with urllib.request.urlopen(req) as response:
            result = json.loads(response.read().decode("utf-8"))
        
        # Print the text answer (which LifeQuery automatically grounds with citations)
Confidence
95% confidence
Finding
The request destination is derived from the untrusted LIFEQUERY_BASE_URL environment variable and then used directly in urllib.request.urlopen, allowing server-side request forgery or exfiltration to an attacker-controlled endpoint if that environment is manipulated. In this skill context, the request contains the user's Telegram query and may include an Authorization bearer token, so redirecting traffic can leak sensitive chat-derived data and credentials.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill documentation indicates use of environment variables and outbound network access, but it does not declare an explicit tool scope such as permissions or allowed-tools. This creates a trust gap: an agent may invoke the skill with broader-than-expected capabilities, enabling unintended access to secrets or exfiltration of Telegram-derived personal data to a remote LifeQuery service. In a privacy-sensitive context like chat history search, undeclared network and env usage is more dangerous because both credentials and highly sensitive user communications may be involved.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The trigger language is overly broad: 'Use when the user wants to search past conversations' and similar phrasing gives the agent wide discretion to access sensitive history without narrowly defined conditions. In a privacy-sensitive skill, vague activation criteria increase the chance of over-collection, unnecessary retrieval of private messages, or use in ambiguous contexts where a safer clarification step should occur first.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill explicitly grants the agent the ability to search the user's entire Telegram chat history, which is highly privacy-sensitive and may include intimate conversations, credentials, links, and personal data about third parties. The metadata does not provide strong user-facing warnings, scoping limits, consent requirements, or access restrictions, so the agent could invoke it in situations where the user does not fully understand the breadth of disclosure.

Static analysis

No suspicious patterns detected.