T09 · Insecure Skill Coding Practices
- Location
scripts/fetch_intelligence.py:164- Finding
ACLED Credentials Exposed Through Request URLs and Error Output
- Content
View full analysis
dict: try: req = urllib.request.Request(url, headers={"User-Agent": "geopolitical-analyst/2.0"}) with urllib.request.urlopen(req, timeout=timeout) as r: return json.loads(r.read().decode()) except Exception as e: return {"error": str(e), "url": url} ``` ```python def acled_events(country: str, api_key: str, email: str, limit: int = 20) -> dict: p = { "key": api_key, "email": email, "country": country, "limit": limit, "fields": "event_date|event_type|sub_event_type|actor1|actor2|location|admin1|fatalities|notes|source", "format": "json", } return _get(f"{ACLED_BASE}?{urllib.parse.urlencode(p)}") ``` ```python parser.add_argument("--acled-key", default=None) parser.add_argument("--acled-email", default=None) ``` ```python if args.acled_key and args.acled_email and args.country: results["sources"]["acled"] = acled_events( args.country, args.acled_key, args.acled_email ) ``` ```python print(json.dumps(results, indent=2, ensure_ascii=False)) ``` ### Technical Analysis The ACLED API key and account email are inserted directly into a GET request's query string. Credential-bearing URLs may be retained in intermediary proxy logs, HTTP diagnostic systems, process monitoring, application telemetry, or other URL-capturing infrastructure. The error-handling behavior creates a direct disclosure path. If `urlopen()` raises an exception, `_get()` returns the complete URL without redacting the `key` or `email` parameters. The returned object is inserted into `results` and subsequently printed as JSON. Consequently, ordinary failures such as timeouts, TLS err ...[truncated 2182 chars]- Remediation
View remediation
str: parsed = urllib.parse.urlsplit(url) params = urllib.parse.parse_qsl(parsed.query, keep_blank_values=True) redacted = [ (name, "[REDACTED]" if name.lower() in {"key", "email"} else value) for name, value in params ] return urllib.parse.urlunsplit( (parsed.scheme, parsed.netloc, parsed.path, urllib.parse.urlencode(redacted), parsed.fragment) ) ``` 3. **Do not include raw request URLs in user-visible errors** ```python except Exception as e: return { "error": str(e), "endpoint": "api.acleddata.com/acled/read/" } ``` 4. **Avoid command-line secret input** - Read the key from a protected environment variable, restricted configuration file, or secret-management facility. - If interactive entry is needed, use `getpass.getpass()` so the credential is not echoed. - Do not print secret values or include them in usage examples. 5. **Control output and logging** - Add centralized secret filtering before JSON serialization. - Ensure exception messages are sanitized because some HTTP exceptions can also contain the request URL. - Document that query topics, countries, app names, and optional ACLED account data are transmitted to third-party services. 6. **Credential response** - Advise users who have captured failed-request output to revoke or rotate the affected ACLED key. ]]>
