T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/synero-council.py:19
- Finding
- Configurable API Endpoint Allows Bearer Credential and Prompt Disclosure<![CDATA[ ## Vulnerability Details **File Location**: `scripts/synero-council.py`, lines 19 and 143–159 **Vulnerability Type**: Unvalidated destination for sensitive network transmission **Risk Level**: Medium ### Technical Analysis The script accepts the complete API destination from the `SYNERO_API_URL` environment variable without validating its scheme or hostname: ```python API_URL = os.environ.get("SYNERO_API_URL", "https://synero.ai/api/query") ``` It subsequently sends the user-supplied prompt and bearer API key to that destination: ```python payload = json.dumps(build_payload(args), ensure_ascii=False).encode("utf-8") req = urllib.request.Request(API_URL, data=payload, method="POST") req.add_header("Content-Type", "application/json") req.add_header("Accept", "text/event-stream") req.add_header( "User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/126.0.0.0 Safari/537.36", ) req.add_header("Authorization", f"Bearer {api_key}") events: dict[str, Any] = {"synthesis": "", "advisor": {}, "complete": None} try: with urllib.request.urlopen(req, timeout=REQUEST_TIMEOUT_SECONDS) as resp: ``` Network transmission is necessary for the Skill’s declared council-query functionality, and the default destination uses HTTPS at `synero.ai`. However, allowing an unrestricted environment variable to replace the entire destination means the same sensitive request can be sent to an arbitrary host or over plaintext HTTP. The request body includes the prompt and may also include thread identifiers, parent query identifiers, and model configuration. The `Authorization` header contains the `SYNERO_API_KEY`. No scheme restriction, hostname allowlist, or explicit warning is applied before these values are transmitted. This is a least-privilege concern because endpoint customization implicitly grants the configured destination access to both the user’s submitted information and a reusable ...[truncated 2001 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Require HTTPS** - Parse the configured URL with `urllib.parse.urlparse`. - Reject URLs whose scheme is not exactly `https`. - Reject malformed URLs, embedded credentials, fragments, and missing hostnames. 2. **Restrict credential destinations** - By default, permit bearer-token transmission only to `synero.ai` or a documented set of trusted Synero API hosts. - If custom enterprise endpoints are required, introduce an explicit allowlist such as `SYNERO_ALLOWED_API_HOSTS`. - Compare normalized hostnames rather than using substring or suffix checks that could accept domains such as `synero.ai.attacker.example`. 3. **Separate endpoint customization from credential forwarding** - Do not automatically attach `SYNERO_API_KEY` to arbitrary custom destinations. - Require a separate, explicit opt-in before forwarding credentials to a non-default host. - Prefer host-specific credential variables when multiple providers or self-hosted deployments are supported. 4. **Prevent unsafe redirects** - Ensure redirects cannot forward the `Authorization` header to an untrusted origin. - Disable redirects for authenticated requests or validate every redirect target before following it. 5. **Warn users about data handling** - Clearly state that prompts, identifiers, model selections, and the API key are transmitted to the configured endpoint. - Warn users not to include secrets or regulated data unless the destination and its retention policy are trusted. 6. **Reduce credential impact** - Use narrowly scoped, revocable, and short-lived API credentials where supported. - Apply server-side rate limits and usage alerts. - Rotate the key immediately if an untrusted endpoint may have received it. A hardened implementation should validate the URL before constructing the authenticated request and terminate with a clear error whenever the destination is not an approved HTTPS origin. ]]>
