T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/meta_ads.py:322
- Finding
- Meta Access Token Can Be Disclosed to Arbitrary Network Destinations## Vulnerability Details **File Location**: `scripts/meta_ads.py:322-365`, with exploitable call paths at `scripts/meta_ads.py:500-519`, `scripts/meta_ads.py:693-705`, and configurable endpoint input at `scripts/meta_ads.py:902-905` **Vulnerability Type**: Unrestricted authenticated request destination and credential disclosure **Risk Level**: High ### Vulnerable Code The URL builder accepts absolute HTTP and HTTPS URLs without validating their destination: ```python def build_url(self, path: str) -> str: if path.startswith("http://") or path.startswith("https://"): return path clean = path.lstrip("/") base = self.graph_base.rstrip("/") if not clean: return f"{base}/{self.api_version}" if clean.startswith(f"{self.api_version}/"): return f"{base}/{clean}" return f"{base}/{self.api_version}/{clean}" ``` The Meta bearer token is then attached unconditionally to the resulting URL: ```python def request( self, method: str, path: str, params: Optional[Dict[str, Any]] = None, files: Optional[Dict[str, Tuple[str, bytes, str]]] = None, ) -> Dict[str, Any]: method = method.upper() url = self.build_url(path) encoded_params = normalise_params(params) data_bytes: Optional[bytes] = None headers: Dict[str, str] = { "Authorization": f"Bearer {self.access_token}", "User-Agent": USER_AGENT, } if method in {"GET", "HEAD"} and not files: url = append_query(url, encoded_params) elif files: body, content_type = build_multipart(encoded_params, files) headers["Content-Type"] = content_type data_bytes = body else: headers["Content-Type"] = "application/x-www-form-urlencoded" data_bytes = urllib.parse.urlencode(encoded_params).encode("utf-8") if encoded_params else b"" last_payload: Any = None last_headers: Dict[str, str] = {} for attempt in range(self.max_retries + 1): req = urllib.request.Req ...[truncated 5647 chars]
- Remediation
- ## Remediation Suggestions 1. **Restrict authenticated requests to trusted Meta origins** - Parse every final URL with `urllib.parse.urlsplit()`. - Require the `https` scheme. - Allow only an explicit hostname allowlist, normally `graph.facebook.com`. - Reject embedded credentials, unexpected ports, malformed hostnames, and non-HTTPS URLs. 2. **Reject absolute user-supplied request paths** - Make the low-level `request` command accept only relative Graph API paths. - Resolve those paths against a validated, fixed Meta Graph API base. - Do not treat `http://` or `https://` values as valid API paths. 3. **Constrain or remove custom Graph bases** - Remove `--graph-base` and `META_GRAPH_BASE` if they are not operationally required. - If custom endpoints are required for testing, require an explicit unsafe-development mode and never reuse production `META_ACCESS_TOKEN` credentials with them. - Prefer a separate test credential for custom Graph-compatible endpoints. 4. **Validate pagination URLs** - Parse each `paging.next` value before following it. - Require it to use HTTPS and match the original validated Meta origin. - Prefer extracting the relative path and approved query parameters rather than directly requesting the supplied absolute URL. 5. **Harden redirect handling** - Disable automatic redirects or implement a redirect handler that revalidates every destination. - Never forward the `Authorization` header when the scheme, hostname, or port changes. - Reject HTTPS-to-HTTP redirects unconditionally. 6. **Add security regression tests** - Verify rejection of arbitrary absolute URLs. - Verify rejection of plain HTTP, foreign hosts, foreign ports, hostname confusion, and cross-origin redirects. - Verify that malicious `paging.next` values are not followed. - Verify that credentials are only attached after destination validation. 7. **Operational response** - Rotate any token that may have been used ...[truncated 214 chars]
