T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/ari.py:57
- Finding
- ARI Bearer Credential Can Be Redirected to an Arbitrary Network Origin<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ari.py:57-71`, `scripts/ari.py:133-160`, `scripts/ari.py:300-320`, and `scripts/ari.py:337-350` **Vulnerability Type**: Authenticated request redirection and credential disclosure **Risk Level**: High ### Vulnerable Code The API origin can be replaced with an arbitrary environment-provided URL: ```python def base_url(): override = (os.environ.get("ARI_BASE_URL") or "").strip().rstrip("/") if not override or override == PROD_BASE: return PROD_BASE if (os.environ.get("ARI_ALLOW_CUSTOM_BASE") or "").strip() != "1": emit(error_obj( "ARI_CUSTOM_BASE_BLOCKED", 0, "ARI_BASE_URL points to a non-official address: %s; request refused" % override, "If this is an intentional development environment, also set " "ARI_ALLOW_CUSTOM_BASE=1.")) raise SystemExit(2) return override ``` Authenticated requests then attach the locally stored production credential to the selected origin: ```python def request_json(method, path, payload=None, params=None): url = base_url() + path if query["params"]: url += "?" + urllib.parse.urlencode(query["params"], doseq=True) data = None if payload is None else json.dumps(payload).encode("utf-8") headers = { "Authorization": "Bearer " + require_key(), "Accept": "application/json", "User-Agent": user_agent(), } if data is not None: headers["Content-Type"] = "application/json" req = urllib.request.Request( url, data=data, headers=headers, method=method ) with urllib.request.urlopen(req, timeout=TIMEOUT_SEC) as resp: raw = resp.read().decode("utf-8") ``` The same behavior exists in the SSE request path: ```python def request_sse(path, payload, recovery_hint=None): url = base_url() + path headers = { "Authorization": "Bearer " + require_key(), "Accept": "text/event-stream", ...[truncated 2921 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Do not reuse production credentials with custom origins.** - If `ARI_BASE_URL` differs from the official origin, require a separate variable such as `ARI_CUSTOM_API_KEY`. - Refuse to fall back to `ARI_API_KEY` or `~/.ari/config.json` in custom-origin mode. 2. **Restrict allowed destinations.** - Parse the URL with `urllib.parse.urlsplit`. - Require the `https` scheme. - Reject embedded usernames or passwords, fragments, unexpected ports, and malformed hostnames. - Use an explicit allowlist of approved development or self-hosted domains. 3. **Prefer build-time endpoint selection.** - Remove runtime custom-origin overrides from distributed production Skills. - Produce a separate development build when custom endpoints are necessary. 4. **Provide visible destination confirmation.** - Before sending credentials to a non-production service, show the normalized destination and require explicit interactive confirmation. - Do not treat an environment variable alone as sufficient authorization. 5. **Separate credential storage by origin.** - Store each credential under a configuration entry keyed by the normalized service origin. - Ensure a credential issued for one origin is never automatically sent to another. 6. **Add security regression tests.** - Verify that production credentials cannot be sent to an arbitrary host. - Verify that HTTP, user-information URLs, unapproved ports, and non-allowlisted domains are rejected. ]]>
