T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/skill_router.py:44
- Finding
- Unvalidated Path Parameter Substitution Allows Same-Origin API Path Manipulation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/skill_router.py`, lines 44–53 **Vulnerability Type**: Improper validation and encoding of URL path parameters **Risk Level**: Medium ### Vulnerable Code ```python parsed = urlparse(url) if parsed.scheme != 'https' or parsed.netloc.lower() != 'openapi.media.io': return {'error': f"Blocked endpoint host: {parsed.netloc}"} headers = { 'X-API-KEY': resolved_api_key, 'Content-Type': 'application/json' } if '{' in url: for k, v in params.items(): url = url.replace(f'{{{k}}}', str(v)) body = {k: v for k, v in params.items() if f'{{{k}}}' not in api['endpoint']} try: resp = requests.request(method, url, headers=headers, json={'data': body} if body else {}, timeout=30) ``` ### Technical Analysis The endpoint allowlist check is performed before user-supplied path parameters are substituted into the URL. The `Task Result` endpoint is defined as: ```text https://openapi.media.io/generation/result/{task_id} ``` The implementation inserts `task_id` using direct string replacement without URL encoding or validation. Consequently, reserved URL characters and path-navigation sequences—such as `/`, `..`, `?`, and `#`—can alter the structure or interpretation of the final request URL. Although the original endpoint is restricted to HTTPS on `openapi.media.io`, this validation does not ensure that the final substituted URL still targets the intended `/generation/result/<task_id>` API route. HTTP client URL normalization may resolve dot segments before sending the request. Query or fragment delimiters may also modify the effective request target. The host remains constrained to `openapi.media.io`, so this is not a general server-side request forgery vulnerability. However, it creates a same-origin endpoint-confusion condition and may cause the caller's API key to be used against an unintended Media.io path. ### Attack Path 1. An attacker supplies parameters to an invocation of the ...[truncated 1602 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Validate each path parameter according to its expected format. For `task_id`, use a strict allowlist such as an API-documented UUID or alphanumeric identifier pattern: ```python import re task_id = params.get("task_id") if not isinstance(task_id, str) or not re.fullmatch(r"[A-Za-z0-9_-]{1,128}", task_id): return {"error": "Invalid task_id format."} ``` 2. Percent-encode path parameter values rather than inserting them directly: ```python from urllib.parse import quote encoded_value = quote(str(v), safe="") url = url.replace(f"{{{k}}}", encoded_value) ``` 3. Reject missing and unresolved placeholders before making the request. 4. Parse and validate the final URL after all substitutions. Confirm the scheme, hostname, port, query, fragment, and expected path prefix: ```python final = urlparse(url) if ( final.scheme != "https" or final.hostname != "openapi.media.io" or final.port not in (None, 443) or final.query or final.fragment or not final.path.startswith("/generation/result/") ): return {"error": "Invalid final endpoint URL."} ``` 5. Prefer explicit request builders for each supported API instead of generic textual URL-template replacement. This allows parameter schemas and permitted routes to be enforced independently. 6. Add tests covering slash characters, dot segments, percent-encoded traversal, query delimiters, fragment delimiters, empty values, oversized identifiers, and non-string parameter values. ]]>
