T09 · Insecure Skill Coding Practices
Error
- Location
- manychat_cli.py:36
- Finding
- Unrestricted API Host Override Exposes the ManyChat Bearer Token and Subscriber Data<![CDATA[ ## Vulnerability Details **File Location**: `manychat_cli.py`, lines 36-49, 122-123, and 246-251 **Vulnerability Type**: Arbitrary credential and sensitive-data transmission **Risk Level**: High ### Vulnerable Code The HTTP client constructs the destination from an unrestricted base URL and attaches the API key to every request: ```python def call(self, endpoint: str, payload: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: if not endpoint.startswith("/"): endpoint = "/" + endpoint url = f"{self.base_url.rstrip('/')}{endpoint}" body = json.dumps(payload or {}).encode("utf-8") req = request.Request( url, data=body, method="POST", headers={ "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json", "Accept": "application/json", }, ) ``` The command-line interface permits users or calling agents to supply an arbitrary destination: ```python parser.add_argument("--api-key", help=f"ManyChat API key (fallback: ${ENV_API_KEY})") parser.add_argument("--base-url", default=None, help=f"API base URL (default: {DEFAULT_BASE_URL} or ${ENV_BASE_URL})") ``` The environment variable or command-line value is accepted without scheme or hostname validation: ```python def get_client(args: argparse.Namespace) -> ManyChatClient: api_key = args.api_key or os.getenv(ENV_API_KEY) if not api_key: raise CLIError(f"Missing API key. Pass --api-key or set {ENV_API_KEY}.") base_url = args.base_url or os.getenv(ENV_BASE_URL) or DEFAULT_BASE_URL return ManyChatClient(api_key=api_key, base_url=base_url, timeout_seconds=args.timeout) ``` ### Technical Analysis The API host override is not restricted to `https://api.manychat.com`, nor is HTTPS required. Regardless of the selected origin, `ManyChatClient.call()` attaches the production ManyChat credential as an `Authorization: Bearer` header. Consequently, an attacker who can i ...[truncated 2402 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Allowlist the production API origin** - Accept `https://api.manychat.com` by default. - Parse the URL with `urllib.parse.urlsplit`. - Reject unexpected schemes, hostnames, ports, user information, query strings, and fragments. 2. **Require HTTPS** - Reject plaintext HTTP destinations to prevent credential interception. - Do not silently normalize or downgrade the scheme. 3. **Restrict development overrides** - Remove `--base-url` and `MANYCHAT_BASE_URL` from normal production operation if they are unnecessary. - If test-server support is required, place it behind an explicit option such as `--allow-untrusted-base-url`. - Display a clear warning and require a separate test credential when that option is used. 4. **Bind credentials to trusted origins** - Attach the authorization header only after confirming that the final request origin is approved. - Prevent credentials from being forwarded if a response redirects to a different origin. - Prefer rejecting redirects for authenticated API requests unless the destination is independently validated. 5. **Validate endpoint paths** - Require relative ManyChat endpoint paths beginning with `/`. - Reject absolute URLs and malformed paths. - Consider allowlisting endpoint prefixes or individual endpoints, especially for agent-driven playbooks and the `raw` command. 6. **Reduce token privileges** - Use a ManyChat credential limited to the operations required by the automation. - Separate read-only lookup credentials from credentials capable of updating subscribers or sending messages where supported. - Rotate the credential immediately if it may have been used with an untrusted base URL. 7. **Add regression tests** - Verify rejection of HTTP URLs, non-ManyChat hosts, embedded credentials, unexpected ports, and cross-origin redirects. - Verify that no authorization header is transmitted before origin validation succeeds. ...[truncated 3 chars]
