T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/virse_call.py:25
- Finding
- Bearer Token Disclosure Through an Unrestricted Configurable API Endpoint## Vulnerability Details **File Location**: `scripts/virse_call.py:25-38, 116-124` **Vulnerability Type**: Credential disclosure through an untrusted network destination **Risk Level**: High ### Vulnerable Code ```python def _read_token(): """Read token: VIRSE_API_KEY env > ~/.virse/token file.""" token = os.environ.get("VIRSE_API_KEY", "").strip() if token: return token try: with open(TOKEN_PATH, "r") as f: return f.read().strip() except FileNotFoundError: return "" def _base_url(): return os.environ.get("VIRSE_BASE_URL", DEFAULT_BASE_URL).rstrip("/") ``` ```python def call_tool(name, args_json): base = _base_url() token = _read_token() endpoint = f"{base}/mcp" headers = {"Content-Type": "application/json", "Accept": "application/json, text/event-stream;q=0.9"} if token: headers["Authorization"] = f"Bearer {token}" ``` The same credential-forwarding behavior appears in `batch_call` at lines 169-177 and `list_tools` at lines 233-241. ### Technical Analysis The script reads a sensitive Virse bearer token from either `VIRSE_API_KEY` or `~/.virse/token`. Independently, it accepts the API destination from the unrestricted `VIRSE_BASE_URL` environment variable. It then attaches the token as an `Authorization` header to requests sent to that destination. The destination is not validated against an approved hostname, HTTPS is not enforced, and no restriction prevents an attacker-controlled origin from being selected. Consequently, any process, wrapper, CI configuration, or execution environment capable of influencing `VIRSE_BASE_URL` can redirect authenticated requests and capture the bearer token. Network transmission of a token to the default Virse endpoint is necessary for the Skill's declared cloud functionality. Allowing that credential to be forwarded to an arbitrary endpoint is not necessary and exceeds minimum privilege. No hardcoded malicious endpoint was identi ...[truncated 1487 chars]
- Remediation
- ## Remediation Suggestions 1. **Restrict authenticated destinations** - Parse the configured URL with `urllib.parse.urlsplit`. - Require the `https` scheme. - Permit bearer-token transmission only to an explicit hostname allowlist, such as `dev.virse.ai`. - Reject embedded credentials, fragments, unexpected ports, malformed hosts, and ambiguous URLs. 2. **Separate production and development credentials** - If custom endpoints are required, require a separate development credential. - Never forward a production Virse token to a custom endpoint. - Require an explicit, clearly named opt-in for custom endpoints. 3. **Harden redirect handling** - Disable redirects for authenticated API requests or validate every redirect target. - Strip the `Authorization` header whenever the destination origin changes. - Reject HTTPS-to-HTTP redirects. 4. **Fail closed** - Abort before reading or attaching the token when endpoint validation fails. - Emit a clear error without printing the credential. - Apply the same centralized validation to `call_tool`, `batch_call`, `list_tools`, and OAuth endpoints. 5. **Update documentation** - Clearly warn that `VIRSE_BASE_URL` must not designate an untrusted service. - Prefer removing the override from ordinary user-facing authentication instructions unless it is operationally required. 6. **Add security tests** - Verify that HTTP endpoints are rejected. - Verify that non-allowlisted hosts do not receive authorization headers. - Verify that cross-origin redirects cannot receive credentials. - Verify that the default approved endpoint continues to work.
