T09 · Insecure Skill Coding Practices
Error
- Location
- src/subtitle.py:140
- Finding
- Authenticated Bilibili session cookies can be disclosed to an unvalidated subtitle URL<![CDATA[ ## Vulnerability Details **File Location**: `src/subtitle.py:140-146`, with authenticated client construction in `src/auth.py:102-107` **Vulnerability Type**: Sensitive credential disclosure through an unvalidated cross-origin request **Risk Level**: Critical ### Complete Code Snippet ```python # src/auth.py:96-107 def get_client(self) -> httpx.AsyncClient: """Create an authenticated async HTTP client. Returns: httpx.AsyncClient configured with credentials. """ return httpx.AsyncClient( headers=self.get_headers(), cookies=self.cookies, timeout=30.0, follow_redirects=True, ) ``` ```python # src/subtitle.py:79-87 for sub in subtitles_info.get("subtitles", []): subtitles.append({ "id": sub.get("id"), "language": sub.get("lan"), "language_name": sub.get("lan_doc"), "url": sub.get("subtitle_url"), "ai_type": sub.get("ai_type", 0), "ai_status": sub.get("ai_status", 0), }) ``` ```python # src/subtitle.py:140-146 # Download subtitle JSON sub_url = target_sub["url"] if sub_url.startswith("//"): sub_url = "https:" + sub_url async with self._get_client() as client: resp = await client.get(sub_url) sub_data = resp.json() ``` ### Technical Analysis The subtitle URL originates in a Bilibili API response and is used as an outbound request destination without validating its scheme or hostname. When a `BilibiliAuth` object is present, `SubtitleDownloader._get_client()` returns the generic authenticated client produced by `BilibiliAuth.get_client()`. That client contains the following sensitive cookies: - `SESSDATA` - `bili_jct` - `buvid3` The client is also configured with `follow_redirects=True`. Consequently, subtitle content is fetched using a client holding account credentials even though downloading the subtitle body does not require authenticated cookies. The implementation does not enforce the domain allowlist declared in ` ...[truncated 1720 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Use a dedicated unauthenticated HTTP client for downloading subtitle content: ```python async with httpx.AsyncClient( headers=DEFAULT_HEADERS, timeout=30.0, follow_redirects=False, ) as client: resp = await client.get(sub_url) ``` 2. Parse and validate every subtitle URL before sending the request: - Require the `https` scheme. - Reject embedded credentials. - Require an explicit allowlist of approved Bilibili subtitle CDN hostnames. - Reject IP literals, localhost, private networks, link-local networks, and metadata endpoints. 3. Disable automatic redirects. If redirects are required, validate the scheme and hostname of every redirect target before following it. 4. Separate network clients by privilege: - Public Bilibili API client without cookies. - Authenticated Bilibili API client with domain-scoped cookies. - Upload client carrying only the upload authorization needed by the relevant endpoint. - YouTube client without Bilibili headers or cookies. 5. Scope cookies explicitly to official Bilibili hosts rather than inserting them into a generic client-level cookie jar. 6. Add regression tests confirming that: - Subtitle CDN requests contain no account cookies. - Non-HTTPS subtitle URLs are rejected. - Unapproved hosts are rejected. - Cross-origin redirects are rejected. - The runtime behavior matches the domain allowlist declared in `skill.json`. ]]>
