T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/dividends.py:107
- Finding
- Unrestricted API Endpoint Override Can Expose the AIsa API Key<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dividends.py`, lines 107–115 **Vulnerability Type**: Unrestricted transmission of credentials to a configurable network destination **Risk Level**: High ### Vulnerable Code ```python def get_client() -> OpenAI: api_key = os.environ.get("AISA_API_KEY") if not api_key: print("❌ Error: AISA_API_KEY environment variable is not set.", file=sys.stderr) print(" Set it with: export AISA_API_KEY=your_key_here", file=sys.stderr) sys.exit(1) base_url = os.environ.get("AISA_BASE_URL", "https://api.aisa.one/v1") return OpenAI(api_key=api_key, base_url=base_url) ``` ### Technical Analysis The Skill legitimately requires `AISA_API_KEY` to authenticate with the default AIsa API at `https://api.aisa.one/v1`. However, `AISA_BASE_URL` can replace that destination with an arbitrary URL without scheme validation, hostname allowlisting, or user confirmation. The `OpenAI` client associates the API key with requests sent to the configured base URL. Consequently, a process environment controlled or influenced by another component can redirect authenticated requests to an attacker-controlled server. This behavior exceeds the minimum privileges needed for the declared functionality because dividend analysis only requires communication with the intended AIsa service, not arbitrary network destinations. The optional endpoint is documented in `SKILL.md`, but documentation does not mitigate credential disclosure when the destination is unrestricted. ### Attack Path 1. An attacker gains influence over the environment used to invoke the Skill, such as through a compromised launcher, CI configuration, wrapper script, container configuration, or unsafe environment-file handling. 2. The attacker sets `AISA_BASE_URL` to an endpoint under their control. 3. A user invokes `scripts/dividends.py` with a valid `AISA_API_KEY`. 4. `get_client()` constructs the API client using the attacker-cont ...[truncated 1058 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `AISA_BASE_URL` support if custom endpoints are not essential to the Skill. 2. If endpoint customization is required, parse the URL and enforce: - HTTPS only. - An explicit allowlist of trusted hostnames. - Expected ports only. - No embedded username or password. - No loopback, link-local, private-network, or metadata-service destinations unless explicitly required. 3. Disable or carefully validate redirects so an approved host cannot redirect an authenticated request to an untrusted destination. 4. Require explicit user confirmation before sending credentials to any non-default endpoint. 5. Use separate, narrowly scoped credentials for development or self-hosted endpoints rather than reusing production AIsa credentials. 6. Avoid logging the API key and ensure exception messages cannot reveal authentication headers. 7. Consider implementing validation similar to: ```python from urllib.parse import urlparse ALLOWED_API_HOSTS = {"api.aisa.one"} base_url = os.environ.get("AISA_BASE_URL", "https://api.aisa.one/v1") parsed = urlparse(base_url) if ( parsed.scheme != "https" or parsed.hostname not in ALLOWED_API_HOSTS or parsed.username is not None or parsed.password is not None or parsed.port not in (None, 443) ): raise ValueError("AISA_BASE_URL is not an approved AIsa API endpoint") ``` ]]>
