Back to skill

Security audit

Bili Summary

Security checks for vulnerabilities and agentic risk

Overview

This skill has a clear video-summary purpose, but it should be reviewed because it can fetch arbitrary URLs and sends transcript text to Gemini with weak consent and credential handling.

Install only if you are comfortable with the skill making network requests and sending video subtitles or Whisper transcripts to Google Gemini for summarization. Use a restricted Gemini API key, prefer a temporary environment variable or a secret manager over adding the key to shell startup files, avoid private or internal URLs, and run the tool in an isolated environment with pinned dependencies where possible.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/bili-summary.py:34
Finding
Unrestricted URL Processing Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bili-summary.py`, lines 34-48; additional affected call sites at lines 100-101, 143-149, 260-266, and 276 **Vulnerability Type**: Server-Side Request Forgery through unrestricted user-supplied URLs **Risk Level**: High ### Vulnerable Code ```python def get_aid_cid(url: str) -> tuple: """从视频URL获取aid和cid""" try: req = urllib.request.Request(url, headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" }) with urllib.request.urlopen(req, timeout=10) as response: html = response.read().decode('utf-8') # 提取 __INITIAL_STATE__ match = re.search(r'window\.__INITIAL_STATE__=(.*?);\(function', html) if match: data = json.loads(match.group(1)) video_data = data.get("videoData", {}) return video_data.get("aid"), video_data.get("cid") # 备用方法:使用 yt-dlp cmd = [YT_DLP, "--dump-json", "--no-download", url] result = subprocess.run(cmd, capture_output=True, text=True) ``` The unrestricted argument is also passed to other network-capable operations: ```python def get_video_info(url: str) -> dict: """获取视频信息""" cmd = [YT_DLP, "--dump-json", "--no-download", url] result = subprocess.run(cmd, capture_output=True, text=True) ``` ```python parser.add_argument("url", help="B站视频URL") ``` ### Technical Analysis The positional argument is described as a Bilibili URL, but the implementation does not enforce that restriction. It performs no validation of: - The URL scheme - The destination hostname - Explicit ports or embedded credentials - The resolved IP address - Redirect destinations - Whether the destination is a loopback, private, link-local, or reserved address `urllib.request.urlopen()` directly opens the supplied URL and may follow redirects. The same value is also passed to the general-purpose `yt-dlp` executable, which supports mor ...[truncated 1823 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Accept only `https` URLs. 2. Apply an explicit hostname allowlist for the Bilibili domains required by the feature. 3. Reject URLs containing user information, fragments, nonstandard ports, or malformed hostnames. 4. Resolve the destination before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 addresses. 5. Disable redirects or validate the scheme, hostname, port, and resolved address of every redirect target. 6. Revalidate immediately before each network operation to reduce DNS rebinding exposure. 7. Apply the same validation before passing a URL to `yt-dlp`; validating only the direct `urllib` request is insufficient. 8. Where possible, convert accepted Bilibili identifiers into application-constructed API URLs instead of opening arbitrary user-provided URLs. 9. Add request-size, download-size, duration, and redirect-count limits. 10. Return clear validation errors rather than silently falling back to another network-capable operation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/bili-summary.py:224
Finding
Gemini API Key Is Exposed in a URL Query Parameter<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bili-summary.py`, lines 224-244 **Vulnerability Type**: Credential exposure through URL-based authentication **Risk Level**: Medium ### Vulnerable Code ```python try: url = f"{LLM_API_URL}?key={GEMINI_API_KEY}" data = { "contents": [{ "parts": [{"text": prompt}] }], "generationConfig": { "temperature": 0.7, "maxOutputTokens": 1000 } } req = urllib.request.Request( url, data=json.dumps(data).encode('utf-8'), headers={"Content-Type": "application/json"} ) with urllib.request.urlopen(req, timeout=60) as response: ``` ### Technical Analysis The key is appropriately read from the `GEMINI_API_KEY` environment variable rather than being hardcoded. However, the implementation appends that secret directly to the request URL: ```text ?key=<GEMINI_API_KEY> ``` URL query strings are more likely than headers to be captured by HTTP infrastructure, proxy logs, tracing systems, exception telemetry, debugging output, or monitoring tools. HTTPS protects the request in transit but does not prevent the complete URL from being recorded at endpoints or trusted intermediaries. The request is sent to the documented Google Gemini endpoint, and no evidence shows intentional credential transmission to an unrelated party. The vulnerability is the unnecessary placement of the credential in a log-prone URL component. ### Attack Path 1. A user configures a valid `GEMINI_API_KEY` environment variable. 2. The summary action constructs a request URL containing the full key. 3. The request passes through local or remote logging, monitoring, proxy, tracing, or error-reporting infrastructure. 4. One of those systems records the complete URL. 5. A person or service with access to the retained logs extracts the API key. 6. The exposed key is used to submit unauthorized Gemini requests until it is revoked or restri ...[truncated 595 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use Google's supported authentication header rather than a query parameter, for example: ```python req = urllib.request.Request( LLM_API_URL, data=json.dumps(data).encode("utf-8"), headers={ "Content-Type": "application/json", "x-goog-api-key": GEMINI_API_KEY, }, ) ``` 2. Ensure exception messages, debug output, telemetry, and request logging redact authentication headers. 3. Restrict the API key to the required Gemini API and only the necessary project or application context. 4. Configure quota and billing alerts to detect unauthorized use. 5. Rotate the existing key if URLs may already have been logged. 6. Avoid printing complete request objects or environment variables during troubleshooting. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:31
Finding
Unpinned Third-Party Dependency Installation Creates Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 31-38; repeated at lines 146-151 and 175 **Vulnerability Type**: Mutable and unverified third-party dependency installation **Risk Level**: Medium ### Vulnerable Code ```bash # Using miniconda3 (recommended) ~/miniconda3/bin/pip install yt-dlp faster-whisper # Or using system Python (may require sudo) pip install yt-dlp faster-whisper ``` The instructions later repeat the same unpinned installation pattern: ```bash ~/miniconda3/bin/pip install yt-dlp faster-whisper # Or use uv uv pip install yt-dlp faster-whisper ``` ### Technical Analysis The installation instructions retrieve `yt-dlp`, `faster-whisper`, and their transitive dependencies without fixed versions or verified artifact hashes. Consequently, the installed code may differ between installations even when the reviewed Skill package has not changed. Python package installation may execute package-controlled build or installation logic. A compromised package release, compromised transitive dependency, package-index incident, or unexpected future release could therefore introduce code that was not included in this audit. The use of `pip` itself is expected for these dependencies, and the package names do not appear to be obvious typosquatting attempts. The confirmed weakness is the absence of reproducible version and integrity controls. ### Attack Path 1. A user follows the documented installation command. 2. The package manager resolves the latest versions available at installation time, including transitive dependencies. 3. A dependency or its distribution channel has been compromised, or a future release contains malicious or unexpectedly unsafe behavior. 4. The package manager downloads and installs that mutable artifact. 5. Package-controlled installation code or imported runtime code executes with the invoking user's privileges. 6. The compromised dependency gains access to files, environment variables, network connec ...[truncated 793 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct dependency to a reviewed exact version. 2. Generate and commit a lockfile that also resolves transitive dependencies. 3. Record cryptographic hashes for approved distributions and install with hash verification, such as `pip --require-hashes`. 4. Specify the intended trusted package index rather than relying on ambient package-manager configuration. 5. Prefer installation inside a dedicated, nonprivileged virtual environment. 6. Remove suggestions that may encourage system-wide or elevated installation where they are not necessary. 7. Review release notes and security advisories before updating pinned versions. 8. Regenerate hashes only after reviewing and testing updated artifacts. 9. Consider automated dependency scanning while ensuring upgrades remain review-gated rather than automatically adopting unreviewed releases. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (17)

Tainted flow: 'req' from os.environ.get (line 251, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, headers={
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })
        with urllib.request.urlopen(req, timeout=10) as response:
            html = response.read().decode('utf-8')
        
        # 提取 __INITIAL_STATE__
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 251, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, headers={
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })
        with urllib.request.urlopen(req, timeout=10) as response:
            html = response.read().decode('utf-8')
        
        # 提取 __INITIAL_STATE__
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 251, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(subtitle_url, headers={
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })
        with urllib.request.urlopen(req, timeout=10) as response:
            data = json.loads(response.read().decode('utf-8'))
            body = data.get("body", [])
Confidence
90% confidence
Finding
The code fetches subtitle_url without validating the scheme or host before issuing an outbound request. Because subtitle_url comes from remote API data, a compromised or malicious upstream response could redirect the tool to arbitrary URLs, enabling SSRF-style access to internal services or unexpected local/network resources depending on runtime environment.

Tainted flow: 'req' from os.environ.get (line 251, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers={"Content-Type": "application/json"}
        )
        
        with urllib.request.urlopen(req, timeout=60) as response:
            result = json.loads(response.read().decode('utf-8'))
            return result.get("candidates", [{}])[0].get("content", {}).get("parts", [{}])[0].get("text", "总结失败")
    except Exception as e:
Confidence
97% confidence
Finding
The script sends transcript/subtitle content and video metadata to an external Gemini API endpoint whenever GEMINI_API_KEY is configured. This is a real data-exposure issue because potentially sensitive or copyrighted content is transmitted off-box without an explicit consent gate at the call site, which is especially relevant in an agent skill context where users may not expect third-party sharing.

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
Gemini API Key

This skill uses **Google Gemini 2.5 Flash** for AI summarization.

**Steps:**
1. Visit https://aistudio.google.com/app/apikey
2. Sign in with your Google account
3. Click "Create API Key"
4. Copy the generated key

**Pricing:** Gemini 2.5 Flash has generous free tier (15 RPM, 1M TPM)

### 3. Set Environment Variable

```bash
# Add to your ~/.bashrc or ~/.zshrc for permanent setup
echo 'export GEMINI_API_KEY="your-api-key-here"' >> ~/.bashrc
source ~/.bashrc

# Or set temporarily for current session
export GEMINI_API_KEY="your-api-key-here"
```

## Quick Start

### Full Workflow (Recommended)

```bash
# Download audio, transcribe, and summarize in one command
uv run {baseDir}/scripts/bili-summary.py "https://www.bilibili.com/video/BV1xx411c7mu" --action summary
```

### Other Actions

```bash
# Get video info only
uv run {baseDir}/scripts/bili-summary.py "URL" --action info

# Download subtitle (if available)
uv run {baseDir}/scripts/bili-summary.py "URL" --action subti
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Session Persistence

Medium
Category
Rogue Agent
Content
**Steps:**
1. Visit https://aistudio.google.com/app/apikey
2. Sign in with your Google account
3. Click "Create API Key"
4. Copy the generated key

**Pricing:** Gemini 2.5 Flash has generous free tier (15 RPM, 1M TPM)
Confidence
86% confidence
Finding
The instructions recommend persisting the Gemini API key in shell startup files such as ~/.bashrc or ~/.zshrc. Persisting secrets in broadly reused shell configuration increases exposure through accidental disclosure, shell-history-assisted inspection, backups, or unintended inheritance by unrelated processes.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill documents that it uses Gemini for summarization, but it does not prominently warn users that extracted subtitles or Whisper-generated transcripts may be transmitted to an external third-party AI service. This creates a real privacy and data-handling risk because users may assume processing is local when potentially sensitive audio-derived text is sent off-device.

External Transmission

Medium
Category
Data Exfiltration
Content
If you want to use other LLMs:

- **OpenAI GPT-4o** - https://api.openai.com/v1/chat/completions
- **Anthropic Claude** - https://api.anthropic.com/v1/messages
- **MiniMax** - https://api.minimax.chat/v1/text/chatcompletion_v2
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
If you want to use other LLMs:

- **OpenAI GPT-4o** - https://api.openai.com/v1/chat/completions
- **Anthropic Claude** - https://api.anthropic.com/v1/messages
- **MiniMax** - https://api.minimax.chat/v1/text/chatcompletion_v2

*Note: Current implementation only supports Gemini. PRs welcome for other providers.*
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
- **OpenAI GPT-4o** - https://api.openai.com/v1/chat/completions
- **Anthropic Claude** - https://api.anthropic.com/v1/messages
- **MiniMax** - https://api.minimax.chat/v1/text/chatcompletion_v2

*Note: Current implementation only supports Gemini. PRs welcome for other providers.*
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 备用方法:使用 yt-dlp
        cmd = [YT_DLP, "--dump-json", "--no-download", url]
        result = subprocess.run(cmd, capture_output=True, text=True)
        if result.returncode == 0:
            info = json.loads(result.stdout)
            return info.get("aid"), info.get("cid")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 备用方法:使用 yt-dlp
        cmd = [YT_DLP, "--dump-json", "--no-download", url]
        result = subprocess.run(cmd, capture_output=True, text=True)
        if result.returncode == 0:
            info = json.loads(result.stdout)
            return info.get("aid"), info.get("cid")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 备用方法:使用 yt-dlp
        cmd = [YT_DLP, "--dump-json", "--no-download", url]
        result = subprocess.run(cmd, capture_output=True, text=True)
        if result.returncode == 0:
            info = json.loads(result.stdout)
            return info.get("aid"), info.get("cid")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 备用方法:使用 yt-dlp
        cmd = [YT_DLP, "--dump-json", "--no-download", url]
        result = subprocess.run(cmd, capture_output=True, text=True)
        if result.returncode == 0:
            info = json.loads(result.stdout)
            return info.get("aid"), info.get("cid")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_subtitle_url(aid: int, cid: int) -> str:
    """获取字幕URL"""
    try:
        url = f"https://api.bilibili.com/x/player/wbi/v2?aid={aid}&cid={cid}"
        req = urllib.request.Request(url, headers={
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
This finding is valid: the summary path forwards large portions of subtitle or transcription text to an external LLM API with no explicit warning or confirmation in the execution flow. In a skill/agent setting, that increases privacy and compliance risk because users may believe processing is local while their content is actually being transmitted to a third party.

Natural-Language Policy Violations

Low
Confidence
98% confidence
Finding
The prompt hard-codes '请用中文回复', which enforces a specific output language regardless of user preference. This is a natural-language locale policy issue because the script does not offer a language choice or document a justified region-specific constraint.

Static analysis

No suspicious patterns detected.