T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/anysearch_cli.py:50
- Finding
- Bearer credentials and sensitive requests can be redirected to an arbitrary endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/anysearch_cli.py:50-84` **Additional Affected Files**: `scripts/anysearch_cli.js:13,56-77`; `scripts/anysearch_cli.ps1:43,53-61,89`; `scripts/anysearch_cli.sh:131-147`; `scripts/generate.py:72,82,92,103` **Vulnerability Type**: Unrestricted API endpoint override with credential forwarding **Risk Level**: High ### Complete Vulnerable Code Snippet ```python API_BASE_URL = os.environ.get( "ANYSEARCH_API_BASE_URL", "https://api.anysearch.com" ).rstrip("/") def _build_headers(api_key: str) -> dict: headers = { "Content-Type": "application/json", "X-Anysearch-Client": CLIENT_HEADER, } if api_key: headers["Authorization"] = f"Bearer {api_key}" return headers def _call_rest(method: str, path: str, api_key: str, *, payload=None, params=None) -> dict: try: resp = requests.request( method, f"{API_BASE_URL}{path}", json=payload, params=params, headers=_build_headers(api_key), timeout=30, ) ``` The Node.js implementation additionally demonstrates that plaintext HTTP is explicitly supported: ```javascript const API_BASE_URL = (process.env.ANYSEARCH_API_BASE_URL || "https://api.anysearch.com").replace(/\/$/, ""); function restRequest(method, endpointPath, apikey, payload = undefined, params = []) { const urlObj = new URL(API_BASE_URL + endpointPath); const options = { hostname: urlObj.hostname, port: urlObj.port || undefined, path: urlObj.pathname + urlObj.search, method, headers: { "Content-Type": "application/json", "X-Anysearch-Client": CLIENT_HEADER, }, }; if (apikey) { options.headers["Authorization"] = `Bearer ${apikey}`; } return new Promise((resolve, reject) => { const transport = urlObj.protocol === "http:" ? http : https; const req = transport.request(options, (res) => { ``` The shell implemen ...[truncated 3330 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Remove the production endpoint override where possible.** Hardcode `https://api.anysearch.com` in distributable clients and inject test endpoints directly into internal test functions. 2. **Enforce HTTPS and an explicit host allowlist.** Validate the parsed URL before constructing any request: ```python from urllib.parse import urlparse parsed = urlparse(API_BASE_URL) if parsed.scheme != "https" or parsed.hostname != "api.anysearch.com": raise RuntimeError("Untrusted AnySearch API endpoint") ``` 3. **Bind credentials to the intended origin.** Add the `Authorization` header only when the final request origin exactly matches the approved HTTPS origin. 4. **Control redirects.** Disable redirects where unnecessary. If redirects are supported, reject cross-origin redirects and never forward authorization headers to a different host or scheme. 5. **Restrict `.env` parsing.** Only import explicitly supported keys: ```python if key == "ANYSEARCH_API_KEY" and value: os.environ[key] = value ``` Do not treat Skill-local `.env` files as a general-purpose source of arbitrary process-environment assignments. 6. **Separate test configuration from production configuration.** For example, permit a custom endpoint only when an explicit test-mode flag is enabled and refuse to attach real credentials in that mode. 7. **Apply the correction consistently.** Update all four clients and `scripts/generate.py` so regeneration does not restore the vulnerable behavior. 8. **Add regression tests** confirming that HTTP URLs, non-AnySearch hosts, user-info URLs, malformed origins, and cross-origin redirects are rejected before credentials are transmitted. ]]>
