T09 · Insecure Skill Coding Practices
- Location
scripts/submit.py:47- Finding
Publishing credential can be transmitted to an unrestricted endpoint
- Content
View full analysis
dict: last_error = None for attempt in range(max_retries + 1): raw_body = json.dumps(payload).encode("utf-8") headers = { "Content-Type": "application/json", "Authorization": f"Bearer {api_key}", **build_submit_signature_headers(api_key, raw_body), } req = urllib.request.Request( api_url, data=raw_body, headers=headers, method="POST", ) try: with urllib.request.urlopen(req, timeout=30) as resp: body = resp.read().decode("utf-8") return json.loads(body) if body else {} ``` The destination is obtained directly from an environment variable without validation: ```python api_url = os.environ.get("VISUAL_STUDIO_API_URL", DEFAULT_API_URL) ``` The same configuration pattern is used by the orchestration scripts: ```python api_url = os.environ.get("VISUAL_STUDIO_API_URL", DEFAULT_API_URL) ``` ### Technical Analysis The publishing client accepts an arbitrary value from `VISUAL_STUDIO_API_URL` and immediately sends an authenticated request to it. There is no validation of: - URL scheme - Destination hostname - Port - Submission path - Embedded URL credentials - Redirect destination The request contains the complete `VISUAL_STUDIO_API_KEY` as a bearer credential. It also includes an HMAC signature derived from the same key. If the destination is changed to an untrusted server, the bearer credential is disclosed directly. Sending the key to the declared OpenFishy submission API is necessary for the Skill's documente ...[truncated 2057 chars]- Remediation
View remediation
str: parsed = urlparse(raw_url) if parsed.scheme != "https": raise ValueError("Submission URL must use HTTPS") if parsed.hostname not in ALLOWED_SUBMIT_HOSTS: raise ValueError("Untrusted submission hostname") if parsed.port not in (None, 443): raise ValueError("Unexpected submission port") if parsed.username or parsed.password or parsed.fragment: raise ValueError("Invalid submission URL") if parsed.path != "/api/submit": raise ValueError("Unexpected submission path") return raw_url ``` ]]>
