Back to skill

Security audit

QWeather City Weather

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it can send a QWeather API key to any configured HTTPS host, which needs user review before installation.

Install only if you are comfortable providing a QWeather API key to this skill. Use a trusted official QWeather API host, prefer protected environment or secret-manager injection over --api-key, and rotate the key if it may have been exposed in command history or logs.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/qweather_query.py:100
Finding
QWeather API key can be disclosed to an unrestricted destination host## Vulnerability Details **File Location**: `scripts/qweather_query.py:100-115`, `scripts/qweather_query.py:151-160` **Vulnerability Type**: Unvalidated network destination receiving a sensitive credential **Risk Level**: Medium ### Vulnerable Code ```python def get_api_host(explicit_host: str | None) -> str: host = explicit_host or environ.get("QWEATHER_API_HOST") if not host: fail("Missing API host. Set --api-host or QWEATHER_API_HOST.") return host def request_json(url: str, api_key: str, timeout_s: float) -> Dict[str, Any]: req = urllib.request.Request( url, headers={ "X-QW-Api-Key": api_key, "Accept": "application/json", "Accept-Encoding": "gzip, deflate", "User-Agent": "qweather-city-weather-skill/1.0", }, ) ``` ```python def search_city(query: str, api_key: str, api_host: str, timeout_s: float, number: int) -> List[Dict[str, Any]]: encoded = urllib.parse.quote(query) url = f"https://{api_host}/geo/v2/city/lookup?location={encoded}&number={number}" data = request_json(url, api_key, timeout_s) locations = data.get("location") or data.get("city") or [] if not isinstance(locations, list): return [] return locations def get_weather(location: str, api_key: str, api_host: str, timeout_s: float) -> Dict[str, Any]: encoded = urllib.parse.quote(location) url = f"https://{api_host}/v7/weather/now?location={encoded}" data = request_json(url, api_key, timeout_s) ``` The unrestricted host behavior is also declared in `SKILL.md:20-22` and `references/qweather-http-contract.md:7-10`. ### Technical Analysis The API host is accepted directly from the `--api-host` argument or `QWEATHER_API_HOST` environment variable. It is interpolated into an HTTPS URL without parsing the value or verifying that the resulting hostname belongs to QWeather. Ever ...[truncated 1860 chars]
Remediation
## Remediation Suggestions 1. Parse the configured value as a hostname rather than interpolating arbitrary text into a URL. 2. Reject user information, paths, query strings, fragments, malformed hostnames, IP literals, and ports not explicitly required by policy. 3. Allow only documented QWeather-owned domains or a narrowly defined administrator-controlled allowlist. 4. Resolve and validate the final request origin before attaching `X-QW-Api-Key`. 5. Disable automatic redirects for credential-bearing requests, or follow redirects only when the scheme remains HTTPS and the destination remains on the validated QWeather origin. 6. Prefer an administrator-controlled full base URL over an unrestricted host supplied on every invocation. 7. Add tests proving that attacker-controlled domains, user-info URL forms, malformed hosts, and cross-origin redirects are rejected before any request containing the API key is transmitted.

T09 · Insecure Skill Coding Practices

Note
Location
SKILL.md:17
Finding
Documentation encourages exposure of the API key through command-line arguments## Vulnerability Details **File Location**: `SKILL.md:17-19`, `SKILL.md:28-43`, `scripts/qweather_query.py:190` **Vulnerability Type**: Sensitive credential accepted and demonstrated as a command-line argument **Risk Level**: Low ### Vulnerable Code ```markdown - Required QWeather API key: - set `QWEATHER_API_KEY`, or - pass `--api-key` ``` ```bash python3 scripts/qweather_query.py search-city --query "Hangzhou" --api-host "<QWEATHER_API_HOST>" --api-key "<QWEATHER_API_KEY>" ``` ```bash python3 scripts/qweather_query.py get-weather --location "101210101" --api-host "<QWEATHER_API_HOST>" --api-key "<QWEATHER_API_KEY>" ``` ```bash python3 scripts/qweather_query.py city-weather --query "Hangzhou" --api-host "<QWEATHER_API_HOST>" --api-key "<QWEATHER_API_KEY>" ``` The corresponding argument is implemented in `scripts/qweather_query.py`: ```python common.add_argument("--api-key", help="QWeather API key (or QWEATHER_API_KEY env).") ``` ### Technical Analysis Secrets supplied as command-line arguments may be exposed through shell history, process inspection interfaces, job-control systems, command logging, CI/CD logs, support bundles, and automation telemetry. Although the script supports the `QWEATHER_API_KEY` environment variable, all primary execution examples explicitly demonstrate `--api-key`, making insecure credential handling the documented default workflow. Environment variables are not universally secret-safe, but they avoid placing the credential directly in command history and process argument lists. A protected secret manager, keyring, configuration file, or non-echoing interactive prompt would provide stronger handling. ### Attack Path 1. A user follows a documented example and substitutes a real API key for the placeholder. 2. The shell records the command in history, or the operating system exposes the process arguments while the script is running ...[truncated 559 chars]
Remediation
## Remediation Suggestions 1. Remove `--api-key` from the primary examples and recommend secret injection through a protected environment or secret manager. 2. Consider reading the API key from a permission-restricted configuration file, operating-system keyring, or non-echoing `getpass` prompt. 3. If command-line key support must remain for compatibility, mark it as discouraged and prominently warn about process-list, shell-history, and logging exposure. 4. Ensure CI/CD examples use masked secret variables and do not enable shell tracing while invoking the script. 5. Document key rotation and revocation procedures for credentials that may have been exposed through command history or logs.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill documents and encourages use of a bundled script that performs outbound network requests, but the manifest declares no tool scope or permission boundary for that capability. This weakens review and runtime governance by hiding external communication behavior from users and policy systems, increasing the chance of unintended data egress or unsafe execution in environments that rely on declared scopes.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs users to send city queries, location identifiers, API host values, and API credentials to an external weather service but does not clearly warn about that data transfer. This is dangerous because users may unknowingly disclose sensitive inputs or direct requests to an untrusted host, especially since the API host is user-configurable and credentials are passed via CLI or environment variables.

Static analysis

No suspicious patterns detected.