T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/ima_voice_create.py:894
- Finding
- API Credential and Prompt Disclosure Through an Arbitrary Base URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ima_voice_create.py`, lines 52–58, 72–79, 611–617, and 894–910 **Vulnerability Type**: Unrestricted credential-bearing network destination **Risk Level**: Medium ### Vulnerable Code ```python def make_headers(api_key: str, language: str = "en") -> dict: return { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", "User-Agent": "IMA-OpenAPI-Client/Skill-1.2.2", "x-app-source": "ima_skills", "x_app_language": language, } ``` ```python url = f"{base_url}/open/v1/product/list" params = {"app": app, "platform": platform, "category": TASK_TYPE} headers = make_headers(api_key, language) resp = requests.get(url, params=params, headers=headers, timeout=30) ``` ```python url = f"{base_url}/open/v1/tasks/create" headers = make_headers(api_key) resp = requests.post(url, json=payload, headers=headers, timeout=30) ``` ```python p.add_argument("--base-url", default=DEFAULT_BASE_URL, help="API base URL") ``` ```python def main(): args = build_parser().parse_args() base = args.base_url # API key is accepted only from environment variable apikey = os.getenv("IMA_API_KEY") if not apikey: logger.error("API key is required. Set IMA_API_KEY environment variable") sys.exit(1) ``` ### Technical Analysis The script obtains the sensitive `IMA_API_KEY` credential from the environment and places it in an HTTP `Authorization: Bearer` header. However, the destination receiving that header is derived directly from the unrestricted `--base-url` command-line argument. There is no validation of: - The destination hostname. - The URL scheme. - The destination port. - URL user-information components. - Whether the destination belongs to an approved IMA domain. Consequently, the credential is not technically restricted to `api.imastudio.com`. A caller can provide an attacker-controlled ...[truncated 2017 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `--base-url` from production-facing command-line options and use the fixed constant: ```python base = DEFAULT_BASE_URL ``` 2. If endpoint configurability is required for controlled testing, enforce an exact allowlist before reading or using the API key: ```python from urllib.parse import urlparse ALLOWED_API_HOSTS = {"api.imastudio.com"} def validate_base_url(value: str) -> str: parsed = urlparse(value) if parsed.scheme != "https": raise ValueError("The API base URL must use HTTPS") if parsed.hostname not in ALLOWED_API_HOSTS: raise ValueError("Unapproved API hostname") if parsed.username or parsed.password: raise ValueError("URL user information is not permitted") if parsed.port not in (None, 443): raise ValueError("Unapproved API port") return f"https://{parsed.hostname}" ``` 3. Validate the destination before constructing any authorization header or sending any request. 4. Disable redirects for credential-bearing requests unless redirects are strictly required: ```python requests.get(..., allow_redirects=False) requests.post(..., allow_redirects=False) ``` 5. If redirects must be supported, validate every redirect destination against the same HTTPS hostname allowlist before resending credentials. 6. Separate authenticated and unauthenticated request helpers so credentials cannot accidentally be attached to arbitrary URLs. 7. Add automated tests confirming that HTTP URLs, alternate hosts, subdomain lookalikes, user-information URLs, and unexpected ports are rejected. 8. Update the documentation only after code-level enforcement matches the claim that credentials are sent exclusively to `api.imastudio.com`. ]]>
