T09 · Insecure Skill Coding Practices
- Location
scripts/security_utils.py:23- Finding
FastFold API key can be disclosed to an arbitrary user-selected endpoint
- Content
View full analysis
Vulnerability Details
File Location:
scripts/security_utils.py:23-35, with the unsafe value consumed by authenticated requests inscripts/create_job.py:78-89,scripts/download_cif.py:32-37,scripts/fetch_results.py:24-29,scripts/get_viewer_link.py:28-33, andscripts/wait_for_completion.py:25-30
Vulnerability Type: Arbitrary credential destination and plaintext bearer-token transmission
Risk Level: HighVulnerable Code
python def validate_base_url(base_url: str) -> str: """Basic safety checks for API base URL.""" parsed = urlparse(base_url) if parsed.scheme not in ("https", "http"): sys.exit("Error: base URL must use http or https.") if not parsed.netloc: sys.exit("Error: base URL must include a host.") if parsed.username or parsed.password: sys.exit("Error: base URL must not include credentials.") if parsed.query or parsed.fragment: sys.exit("Error: base URL must not include query or fragment.") return f"{parsed.scheme}://{parsed.netloc}{parsed.path}".rstrip("/")The validated value is then used for authenticated requests. For example,
scripts/create_job.py:78-89contains:python url = f"{base_url.rstrip('/')}/v1/jobs" if from_id: url = f"{url}?from={from_id}" headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} req = urllib.request.Request( url=url, data=json.dumps(body).encode("utf-8"), headers=headers, method="POST", ) try: with urllib.request.urlopen(req, timeout=30) as resp: response_text = resp.read().decode("utf-8", errors="replace") status = resp.getcode()Technical Analysis
All authenticated scripts expose a
--base-urlargument. The sharedvalidate_base_url()function accepts any host and permits both HTTPS and plaintext HTTP. The scripts subsequently attach the value ofFASTFOLD_API_KEY...[truncated 2381 chars]- Remediation
View remediation
Remediation Suggestions
- Require HTTPS for every authenticated API request. Remove support for plaintext HTTP.
- Allowlist
api.fastfold.aias the default and only permitted host for production credentials. - If alternate deployments are genuinely required, separate their credentials from
FASTFOLD_API_KEYand require explicit trusted-host configuration rather than accepting an unrestricted CLI URL. - Disable automatic redirects for authenticated requests, or validate every redirect destination before following it. Never preserve the authorization header across a host change.
- Reject IP literals, embedded user information, unexpected ports, and non-allowlisted subdomains unless specifically required and approved.
- Update
references/auth_and_api.mdto state that production FastFold credentials must never be sent to alternate endpoints. - Add regression tests proving that HTTP URLs, attacker-controlled domains, deceptive subdomains, and cross-host redirects are rejected.
A strict production validator could use the following policy:
python def validate_base_url(base_url: str) -> str: parsed = urlparse(base_url) if parsed.scheme != "https": sys.exit("Error: API base URL must use HTTPS.") if parsed.hostname != "api.fastfold.ai": sys.exit("Error: authenticated API host is not trusted.") if parsed.username or parsed.password: sys.exit("Error: base URL must not include credentials.") if parsed.port not in (None, 443): sys.exit("Error: unexpected API port.") if parsed.query or parsed.fragment: sys.exit("Error: base URL must not include query or fragment.") return "https://api.fastfold.ai"
