T09 · Insecure Skill Coding Practices
- Location
scripts/weather.py:104- Finding
Unvalidated API Host Allows Credential and Location Disclosure
- Content
View full analysis
Vulnerability Details
File Location:
scripts/weather.py, lines 42, 104-106, and 205-209
Vulnerability Type: Unvalidated network destination and sensitive query-string transmission
Risk Level: HighVulnerable Code
python QW_HOST = os.environ.get("QWEATHER_API_HOST", "")python def _resolve_city_name(name): """通过 QWeather Geo API 将城市名解析为 CityId.""" url = f'https://{QW_HOST}/geo/v2/city/lookup' params = {'key': QW_KEY, 'location': name} data = _json_fetch(url + '?' + urllib.parse.urlencode(params))python def fetch_qweather(endpoint_key, location, lang='zh', unit='m'): """请求和风天气 API.""" ep = QW_ENDPOINTS.get(endpoint_key, f'v7/{endpoint_key}') url = f'https://{QW_HOST}/{ep}' params = {'key': QW_KEY, 'location': location, 'lang': lang, 'unit': unit} return _json_fetch(url + '?' + urllib.parse.urlencode(params))Technical Analysis
The script obtains
QW_HOSTfrom theQWEATHER_API_HOSTenvironment variable and interpolates it directly into HTTPS request URLs. It verifies only that the value is nonempty; it does not establish that the hostname is an authorized QWeather domain.Consequently, any party able to influence the process environment or deployment configuration can redirect weather and city-lookup requests to an arbitrary HTTPS server. The requests include both
QW_KEYand the user-supplied location in the URL query string.TLS only protects the connection to the selected destination. It does not prevent disclosure when the selected destination itself is attacker-controlled. Query-string credentials may also be retained in destination server logs, reverse-proxy logs, monitoring systems, or other URL-processing infrastructure.
This network access is generally necessary for the Skill's declared weather functionality, but permitting an unrestricted credential destination exceeds minimum privilege. The WAQI request doe ...[truncated 1715 chars]
- Remediation
View remediation
Remediation Suggestions
-
Validate the configured destination against an explicit trust policy.
- Parse the value as a hostname rather than interpolating an unrestricted string.
- Require an approved QWeather domain or documented domain suffix.
- Reject IP literals, URL schemes, user-information components, ports, paths, query strings, fragments, control characters, and malformed hostnames.
- Perform suffix validation on DNS label boundaries; a simple substring or
endswith("qweatherapi.com")check may accept deceptive domains if implemented incorrectly.
-
Prefer a fixed API origin where operationally possible.
- If QWeather supports a stable endpoint, hardcode it.
- If account-specific hosts are required, provision the approved hostname through trusted installation configuration and validate it before every request.
-
Avoid credentials in URL query strings where supported.
- Use the provider-supported authorization header or another non-URL authentication mechanism.
- Ensure error messages and debug logs never print authentication values.
-
Fail closed on invalid configuration.
- Terminate before making a request when the host does not satisfy the allowlist.
- Do not silently normalize or accept unexpected host syntax.
-
Constrain redirects.
- Reject redirects to unapproved hosts so credentials or sensitive request data cannot be forwarded outside the trusted destination set.
-
Limit credential impact.
- Use a dedicated key with the minimum required API permissions and quota.
- Rotate the key if it may have been used with an untrusted host.
- Monitor usage for unexpected requests and quota consumption.
Example validation should conceptually enforce a trusted, syntactically valid hostname before constructing the URL:
python import re HOST_RE = re.compile( r"^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?" r"(?:\.[a-z0-9](?:[a-z0-9 ...[truncated 616 chars]-
