T09 · Insecure Skill Coding Practices
- Location
scripts/create_and_wait.py:63- Finding
Bearer API Key Can Be Exfiltrated Through Attacker-Controlled Request URLs
- Content
View full analysis
Vulnerability Details
File Location:
scripts/create_and_wait.py:63-68, 161-169, 221, 324-325, 334-364;scripts/wait_generation.py:85-97, 107, 118, 153-159
Vulnerability Type: Unrestricted authenticated network destination
Risk Level: HighVulnerable Code
In
scripts/create_and_wait.py, an endpoint may be supplied as an unrestricted full URL:python def endpoint_url(base_url: str, endpoint: str) -> str: if endpoint.startswith("http://") or endpoint.startswith("https://"): return endpoint if not endpoint.startswith("/"): endpoint = "/" + endpoint return f"{base_url.rstrip('/')}{endpoint}"The API key is attached to the resulting destination without validating its hostname or transport security:
python req = request.Request( url, headers={ "Authorization": f"Bearer {api_key}", "Accept": "text/event-stream", "Content-Type": "application/json", }, data=json.dumps(payload).encode("utf-8"), method="POST", )python with request.urlopen(req, timeout=request_timeout) as resp:Both the endpoint and base URL are caller-controlled:
python parser.add_argument("--sse-endpoint", required=True, help="SSE create endpoint path or full URL") parser.add_argument("--base-url", default="https://open.skills.video/api/v1")python api_key = os.environ.get("SKILLS_VIDEO_API_KEY", "").strip() payload = load_payload(args) url = endpoint_url(args.base_url, args.sse_endpoint) sse_rc, generation_id, terminal_payload = run_sse( url=url, api_key=api_key, payload=payload, request_timeout=args.sse_request_timeout, )The polling helper in
scripts/wait_generation.pyhas the same trust-boundary issue:python def fetch_generation( base_url: str, generation_id: str, api_key: str, request_timeout: float, ) -> tuple[int, An ...[truncated 3757 chars]- Remediation
View remediation
Remediation Suggestions
-
Remove support for unrestricted absolute endpoint URLs. Require
--sse-endpointto be a relative API path beginning with an approved prefix such as/generation/sse/. -
Validate the final URL before attaching credentials. Parse it with
urllib.parse.urlsplitand require:- Scheme exactly equal to
https. - Hostname exactly equal to
open.skills.video. - No embedded username or password.
- No unexpected port.
- No IP-literal or deceptive suffix hostname.
- Scheme exactly equal to
-
Restrict or remove
--base-url. If custom environments are required, make them an explicit opt-in mode and require a separate credential intended for that origin. Do not reuse the production key automatically. -
Constrain redirects. Disable automatic redirects for authenticated calls or implement a redirect handler that permits redirects only when the destination remains on the same approved HTTPS origin. Strip the
Authorizationheader before any cross-origin redirect. -
Apply one shared origin-validation function in both
create_and_wait.pyandwait_generation.pybefore constructing an authenticated request. -
Fail closed. Reject malformed URLs, plaintext HTTP, scheme-relative URLs, unexpected ports, and any origin not explicitly approved.
-
Add security regression tests covering:
http://open.skills.videohttps://attacker.examplehttps://open.skills.video.attacker.example- URLs containing user-info
- IP-address destinations
- Cross-origin redirects
- Valid relative paths on
https://open.skills.video
-
Rotate exposed credentials. If either helper has been run with an untrusted URL, revoke the affected API key, issue a new one, and review API usage and credit consumption.
-
