T09 · Insecure Skill Coding Practices
- Location
scripts/bing_search.py:453- Finding
Arbitrary Endpoint Override Exposes the Dataify API Token
- Content
View full analysis
str: if args.url: return args.url return DEFAULT_ENDPOINT def resolve_token(args: argparse.Namespace) -> str: token = args.token or os.getenv("DATAIFY_API_TOKEN") if not token: raise ValueError( "缺少 DATAIFY_API_TOKEN。请提供 --token,或设置 DATAIFY_API_TOKEN," "或访问 https://dashboard.dataify.com/login?utm_source=skill 注册获取;新账号注册即得 50 免费积分。" ) token = token.strip() if not token.lower().startswith("bearer "): token = f"Bearer {token}" os.environ["DATAIFY_API_TOKEN"] = token return token def call_api(url: str, token: str, payload: dict[str, str], body_format: str, timeout: float) -> tuple[bool, str]: headers = { "Authorization": token, "Accept": "application/json, text/plain;q=0.9, */*;q=0.8", "User-Agent": "codex-bing-search-skill/1.0", } if body_format == "json": data = json.dumps(payload).encode("utf-8") headers["Content-Type"] = "application/json" else: data = urllib.parse.urlencode(payload).encode("utf-8") headers["Content-Type"] = "application/x-www-form-urlencoded" request = urllib.request.Request(url, data=data, headers=headers, method="POST") try: with urllib.request.urlopen(request, timeout=timeout) as response: raw = response.read() text = raw.decode(response.headers.get_content_charset() or "utf-8", errors="replace") return 200 <= response.status < 300, text except urllib.error.HTTPError as exc: text = exc.read().decode("utf-8", errors="replace") return False, text or f"HTTP {exc.code}: {exc.reason}" except urllib.error.URLError as exc: r ...[truncated 3061 chars]- Remediation
View remediation
str: return DEFAULT_ENDPOINT ``` 2. **If an override is operationally necessary, enforce an exact allowlist.** Parse and validate the URL before retrieving or attaching the token: ```python from urllib.parse import urlsplit ALLOWED_SCHEME = "https" ALLOWED_HOST = "scraperapi.dataify.com" ALLOWED_PATH = "/request" def resolve_url(args: argparse.Namespace) -> str: candidate = args.url or DEFAULT_ENDPOINT parsed = urlsplit(candidate) if ( parsed.scheme != ALLOWED_SCHEME or parsed.hostname != ALLOWED_HOST or parsed.port not in (None, 443) or parsed.path != ALLOWED_PATH or parsed.username is not None or parsed.password is not None ): raise ValueError("The API endpoint is not an approved Dataify endpoint.") return candidate ``` 3. **Bind credentials to the expected origin.** Add the `Authorization` header only after confirming that the final request destination is the approved HTTPS origin. 4. **Prevent cross-origin credential forwarding.** Disable automatic redirects or validate every redirect target before following it. Never forward the authorization header to a different origin. 5. **Separate development and production credentials.** If arbitrary endpoints are required for local debugging, use an explicit development-only mode with a non-production test token rather than `DATAIFY_API_TOKEN`. 6. **Do not expose a general endpoint override in Skill instructions.** Remove `--url` from ordinary usage documentation so untrusted prompts cannot readily induce credential-bearing requests to external destinations. 7. **Add regression tests** confirming that HTTP URLs, alternate hosts, deceptive subdomains, ...[truncated 142 chars]
