Back to skill

Security audit

LibTV Skill Pro

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its LibTV media-creation purpose, but its download/export helpers have unsafe network and file-write behavior that should be reviewed before installation.

Install only if you trust the publisher and will use it deliberately for LibTV work. Keep LIBTV_ACCESS_KEY secret, avoid uploading private media unless intended, do not use the direct --urls download option with untrusted URLs, avoid path-like filename prefixes, and treat exported HTML reports as untrusted files.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/download_results.py:58
Finding
Unrestricted URL Fetching Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/download_results.py`, lines 58–71, 92, 101, and 130 **Vulnerability Type**: Server-Side Request Forgery and unbounded remote-content download **Risk Level**: High ### Vulnerable Code ```python def download_file(url, filepath): """Download a single file.""" req = urllib.request.Request(url, headers={"User-Agent": "LibTV-Skill/1.0"}) try: with urllib.request.urlopen(req, timeout=60) as resp: with open(filepath, "wb") as f: while True: chunk = resp.read(8192) if not chunk: break f.write(chunk) return filepath, None except Exception as e: return filepath, str(e) ``` The command-line interface accepts URLs directly and submits each URL to this function: ```python parser.add_argument( "--urls", nargs="+", default=[], help="Directly specify a list of URLs to download", ) urls = list(args.urls) # ... futures = { pool.submit(download_file, url, fp): (url, fp) for url, fp in tasks } ``` ### Technical Analysis The `--urls` option accepts arbitrary user-controlled URLs. These URLs are passed directly to `urllib.request.urlopen()` without validating: - The URL scheme - The destination hostname - The resolved IP address - Redirect destinations - Whether the target is a loopback, link-local, private, multicast, or reserved address - The response media type - The response size Although URLs extracted from assistant text are partially constrained by a LibTV-domain regular expression, the documented `--urls` path bypasses that restriction entirely. Python's `urllib` also follows HTTP redirects by default, so validation limited to an initial URL would not be sufficient. The vulnerability is especially relevant in an Agent Skill because an untrusted prompt can induce the Agent to invoke the documented download command with attacker-sel ...[truncated 1515 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict downloads to `https` URLs. 2. Use an explicit hostname allowlist, such as the exact approved LibTV result host. 3. Resolve the hostname before connecting and reject every address that is loopback, link-local, private, multicast, unspecified, or reserved. 4. Disable redirects or validate the scheme, hostname, and resolved address after every redirect. 5. Reject URLs containing embedded credentials or ambiguous hostname encodings. 6. Enforce an allowlist of expected media content types. 7. Set a maximum response size and stop writing when the limit is reached. 8. Apply both connection and read deadlines. 9. Download to a temporary file and atomically move it into place only after all checks succeed. 10. Consider removing arbitrary `--urls` support if downloading non-LibTV resources is not required. Example validation policy: ```python from urllib.parse import urlparse import ipaddress import socket ALLOWED_HOSTS = {"libtv-res.liblib.art"} MAX_DOWNLOAD_BYTES = 500 * 1024 * 1024 def validate_download_url(url): parsed = urlparse(url) if parsed.scheme != "https": raise ValueError("Only HTTPS URLs are permitted") if parsed.hostname not in ALLOWED_HOSTS: raise ValueError("Unapproved download host") for result in socket.getaddrinfo(parsed.hostname, 443): address = ipaddress.ip_address(result[4][0]) if ( address.is_private or address.is_loopback or address.is_link_local or address.is_multicast or address.is_reserved or address.is_unspecified ): raise ValueError("Unsafe destination address") ``` Equivalent checks must be repeated for redirect targets, and DNS rebinding should be mitigated by connecting to the validated address or otherwise binding validation to the actual connection. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/download_results.py:113
Finding
Unsanitized Filename Prefix Allows Writes Outside the Output Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/download_results.py`, lines 93 and 113–124 **Vulnerability Type**: Path traversal and arbitrary file overwrite **Risk Level**: High ### Vulnerable Code ```python parser.add_argument( "--output-dir", default="", help="Output directory", ) parser.add_argument( "--prefix", default="", help="Filename prefix", ) # Prepare output directory output_dir = args.output_dir or os.path.expanduser("~/Downloads/libtv_results") os.makedirs(output_dir, exist_ok=True) # Build download tasks tasks = [] for i, url in enumerate(urls, 1): ext = os.path.splitext(url.split("?")[0])[-1] or ".png" if args.prefix: filename = f"{args.prefix}_{i:02d}{ext}" else: filename = f"{i:02d}{ext}" filepath = os.path.join(output_dir, filename) tasks.append((url, filepath)) ``` The resulting path is later opened for writing: ```python with open(filepath, "wb") as f: while True: chunk = resp.read(8192) if not chunk: break f.write(chunk) ``` ### Technical Analysis `args.prefix` is incorporated into a filename without removing path separators, rejecting absolute paths, normalizing the destination, or verifying that the final path remains beneath `output_dir`. For example, a prefix containing `../../target` produces a path similar to: ```text /output/directory/../../target_01.ext ``` The operating system resolves the traversal components when the file is opened, allowing the write to escape the intended output directory. An absolute prefix can also cause `os.path.join()` to discard the configured output directory. The file is opened using `"wb"`, so an existing target is silently truncated and overwritten. When combined with the arbitrary URL download feature, the attacker controls both the destination path and the written content. ### Attack Path 1. An attacker supplies or induces an Agent invocation containing: - An attacker-c ...[truncated 1330 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict prefixes to a conservative filename character set, such as letters, digits, underscores, and hyphens. 2. Explicitly reject `/`, `\`, null bytes, traversal components, and absolute paths. 3. Derive the filename with `os.path.basename()` after validation. 4. Resolve both the output directory and final destination using `pathlib.Path.resolve()`. 5. Verify that the final destination is a descendant of the resolved output directory. 6. Use exclusive file creation by default to prevent silent overwrites. 7. Provide an explicit, separately authorized overwrite option if overwriting is required. 8. Consider using fixed generated filenames rather than accepting path-like prefixes. Example hardening: ```python import re from pathlib import Path SAFE_PREFIX = re.compile(r"^[A-Za-z0-9_-]{1,100}$") def safe_destination(output_dir, prefix, index, extension): if prefix and not SAFE_PREFIX.fullmatch(prefix): raise ValueError("Invalid filename prefix") base = Path(output_dir).expanduser().resolve() base.mkdir(parents=True, exist_ok=True) filename = f"{prefix + '_' if prefix else ''}{index:02d}{extension}" destination = (base / filename).resolve() if destination.parent != base: raise ValueError("Destination escapes output directory") return destination ``` Files should be created with mode `"xb"` or an equivalent exclusive-create operation unless the user explicitly authorizes replacement. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/export_results.py:118
Finding
Unescaped Session Content Causes Stored HTML Injection in Exported Reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/export_results.py`, lines 118–164 **Vulnerability Type**: Stored HTML injection and cross-site scripting **Risk Level**: Medium ### Vulnerable Code Media URLs extracted from session messages are inserted directly into HTML attributes: ```python # Images if data['urls']['images']: html_parts.append("<h3>Images</h3><div>") for url in data['urls']['images']: html_parts.append(f'<img src="{url}" loading="lazy">') html_parts.append("</div>") # Videos if data['urls']['videos']: html_parts.append("<h3>Videos</h3><div>") for url in data['urls']['videos']: html_parts.append(f'<video src="{url}" controls preload="metadata"></video>') html_parts.append("</div>") ``` Session fields and message contents are also inserted without escaping: ```python # Messages html_parts.append("<h2>Message History</h2>") for msg in data['messages']: role = msg.get("role", "unknown") content = msg.get("content", "") seq = msg.get("seq", 0) content_html = content.replace("\n", "<br>") html_parts.append(f'<div class="message {role}">') html_parts.append(f'<div class="meta">[{seq}] {role.upper()}</div>') html_parts.append(f'<div>{content_html}</div>') html_parts.append('</div>') ``` Other session metadata is interpolated in the same way: ```python f"<title>Session Export - {data['session_id'][:8]}</title>", f"<p><strong>Session ID:</strong> {data['session_id']}</p>", f"<p><strong>Export Time:</strong> {data['export_time']}</p>", ``` ### Technical Analysis The HTML exporter treats untrusted session values as trusted markup. Replacing newlines with `<br>` does not escape HTML metacharacters such as `<`, `>`, `"`, `'`, and `&`. A malicious message can therefore include HTML elements, event-handler attributes, or script-capable markup. When the exported report is opened in a browser, the payload is interpreted as active HTML rather than displayed as plain ...[truncated 1885 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape every untrusted value with `html.escape(value, quote=True)` before placing it in HTML text or attributes. 2. Validate roles against a fixed allowlist such as `user`, `assistant`, `tool`, and `system`. 3. Validate media URLs and allow only HTTPS URLs from approved LibTV media hosts. 4. Do not rely on regular expressions alone for safe HTML attribute construction. 5. Add a restrictive Content Security Policy that blocks scripts and limits network and media origins. 6. Consider omitting active media previews and rendering URLs as escaped text links. 7. Use an HTML templating engine with automatic escaping enabled. Example hardening: ```python import html from urllib.parse import urlparse ALLOWED_ROLES = {"user", "assistant", "tool", "system"} ALLOWED_MEDIA_HOSTS = {"libtv-res.liblib.art"} def escape_text(value): return html.escape(str(value), quote=True) def safe_role(value): return value if value in ALLOWED_ROLES else "unknown" def safe_media_url(value): parsed = urlparse(value) if parsed.scheme != "https" or parsed.hostname not in ALLOWED_MEDIA_HOSTS: raise ValueError("Unapproved media URL") return html.escape(value, quote=True) role = safe_role(msg.get("role", "unknown")) content_html = escape_text(msg.get("content", "")).replace("\n", "<br>") seq_html = escape_text(msg.get("seq", 0)) html_parts.append(f'<div class="message {role}">') html_parts.append(f'<div class="meta">[{seq_html}] {escape_text(role.upper())}</div>') html_parts.append(f'<div>{content_html}</div>') ``` A suitable defense-in-depth policy for a standalone report would be similar to: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https://libtv-res.liblib.art; media-src https://libtv-res.liblib.art; style-src 'unsafe-inline';"> ``` ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (57)

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

Critical
Category
Data Flow
Content
headers=_headers(),
    )
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            return json.loads(resp.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        err_body = e.read().decode("utf-8") if e.fp else ""
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 116, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers=_headers(),
    )
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            return json.loads(resp.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        err_body = e.read().decode("utf-8") if e.fp else ""
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
声明描述的是一个功能完整的 AI 图片/视频生成编辑工具,而提供的代码片段只是底层公共辅助模块,围绕 IM/OpenAPI 会话与项目管理展开:POST/GET 请求、Bearer 鉴权、结构化错误处理、创建会话、查询会话、切换项目,以及在 ~/.libtv_projects.json 中保存项目记录。虽然这可能是更大技能的一部分,并且其中“会话历史/项目管理”与声明略有关联,但从该代码片段本身看,核心行为与声明的主要用途明显不一致,缺少任何实际媒体生成或编辑实现,因此应判定为描述与代码行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
该描述将技能定位为 LibTV 的完整 AI 图片/视频生成编辑工具集,核心用途应是媒体内容创作与处理。但实际代码片段仅调用 `/openapi/session/change-project` 接口来切换当前 accessKey 绑定的项目,并保存项目信息。这属于项目/会话管理能力,不是所宣称的图像视频生成或编辑功能。虽然描述中提到 Pro 版包含‘项目管理’,因此项目切换可以算相关辅助能力,但就这段代码本身而言,其主要目的与宣称的核心能力明显不一致,且触发词也不匹配,因此应判定为描述与实际行为存在不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
声明描述的是一个覆盖广泛的 AI 图片/视频生成与编辑平台工具集,核心用途应是调用 LibTV 进行内容创作与管理。但代码仅执行下载功能:查询会话、解析消息中的 task_result 或 assistant 文本中的资源 URL,并把图片/视频保存到本地。虽然“结果导出”在声明中被提及,下载脚本可被视为相关辅助能力,但这段代码的主要目的明显不是生成或编辑媒体内容,而是离线导出既有结果。故该代码块与声明的主功能存在实质性不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
代码的核心用途是“导出会话结果”,属于会话历史/结果导出辅助工具,而非描述中宣称的 AI 媒体生成与编辑工具集。虽然声明里提到 Pro 版包含“结果导出、会话历史”,这与代码片段部分相关,但该片段的实际主功能仅限于查询 session、统计消息、提取媒体 URL、格式化导出报告。它没有实现描述中强调的大多数核心能力,例如文生图/图生视频、视频续写、局部编辑、模型切换、分辨率/时长参数控制等。因此该代码片段与声明的主要用途存在实质性不一致,应判定为 mismatch。

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
该代码的实际用途较窄:它是一个 CLI 入口,用于将用户输入包装成 4 种 LibTV 预设 prompt 并调用 create_session。虽然与 LibTV 相关,且包含 dry-run、会话 ID 复用、项目记录等少量声明中提到的元素,但远不足以支撑“完整工具集”的描述。声明列举了广泛的图像/视频生成编辑能力、模型与参数控制以及 Pro 特性,而此代码片段没有展示这些核心能力,主功能明显更受限,因此描述与行为存在实质性不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a broad AI image/video generation and editing toolset for LibTV, with support for many creative operations and model parameters. The supplied code chunk does not implement any media generation or editing behavior. Instead, it is narrowly focused on project/session management: listing local project records, showing the current project, switching projects through a server-side change-project API, recording metadata locally, and removing/describing project entries. While the description briefly mentions project management as a Pro feature, this specific code chunk’s primary and only evident purpose is project management, not the advertised generation/editing toolkit. Therefore, the chunk does not accurately represent the declared purpose and should be flagged as a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
声明描述的是一个功能广泛的 AI 图像/视频生成编辑工具套件,核心应包含媒体生成、编辑、模型调用和项目级管理能力。而给出的代码仅实现了对会话消息的监控与输出:调用 query_session 拉取消息、可轮询、过滤角色、格式化显示、提取 URL、保存日志。虽然声明中提到 Pro 版包含“轮询监控、结果导出、会话历史”等周边能力,但当前代码片段本身并未体现所宣称的主要生成/编辑功能,且其主要用途明显偏向会话日志监控。因此该代码片段与整体声明存在明显描述-行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
该代码与声明的领域方向相关,确实属于 LibTV 的 AIGC 节点能力封装,并包含首尾帧、参考素材生成、图生图、图片高清、dry-run、会话/项目记录等部分内容。但声明明显夸大了覆盖范围:代码只实现一个 CLI 包装层,通过固定模板向会话接口发送 prompt,并没有实现声明中的“大量生成/编辑能力”和模型参数控制,也没有体现 Pro 版的大部分扩展能力。尤其“文生视频”在代码中仅是提示词改写,不是直接生成视频;“文字生音乐”也只是构造消息而非展示完整音乐产出流程。因此描述不能准确代表该代码块的实际能力,属于能力范围被显著高估的 mismatch。

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
声明描述的是一个覆盖广泛的 LibTV AI 图像/视频生成与编辑平台工具集,核心能力应围绕媒体生成、编辑和项目化工作流。实际代码片段的唯一功能是查询会话消息列表/进展,属于会话监控类辅助脚本,而不是生成或编辑媒体内容。虽然“轮询监控/会话历史”在声明中被提及,当前代码最多只与这类辅助能力部分相关,但它并不能代表声明中的主要用途,且与宣称的完整生成编辑工具集相比范围明显过窄。因此应判定为描述与代码行为存在实质性不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents the skill as a full-featured LibTV media generation/editing toolkit with broad creation capabilities across images, videos, workflows, and model selection. The supplied code chunk does not generate or edit any media, invoke models, construct prompts, manage projects, or expose those creative operations. Instead, it implements a simple CLI helper that polls a session via query_session until an assistant reply appears to indicate completion, then prints or saves that message. While polling/monitoring is mentioned in the broader Pro description, this code chunk's primary purpose is narrowly a session-status polling script, which is materially different from the declared overall generation/editing functionality. Therefore the description does not accurately represent what this specific code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description centers on AI media generation/editing capabilities through the LibTV platform. However, this code chunk does not generate or edit images or videos, call LibTV APIs, invoke models, manage workflows, or perform any of the listed creative/media tasks. Its sole purpose is maintaining local session history records via a command-line interface and a JSON file in the user's home directory. While session history could be a supporting feature in a broader tool, this chunk's actual behavior is materially different from the declared primary purpose and includes undeclared local filesystem-based history management. Therefore this code chunk does not accurately represent the declared functionality.

Vague Triggers

High
Confidence
97% confidence
Finding
The trigger phrases are extremely broad, including common verbs like '生成', '做一个', and '帮我做', which can match many ordinary conversations unrelated to this skill. In an agent ecosystem, that can cause unintended invocation of a networked, file-capable skill, leading to accidental API calls, unwanted uploads/downloads, quota burn, and exposure of local paths or user content to an external service.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises and documents capabilities that require network access, environment variable access, and local file read/write, but it does not declare an explicit tool scope such as allowed-tools or permissions. That creates an over-privileged, under-specified execution boundary where an agent runtime may grant broader access than users expect, increasing the blast radius of prompt mistakes or abuse.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The natural-language description and trigger definitions are entirely in Chinese and present the skill as operating in that language, but there is no indication that users may choose another language. This creates a locale-policy concern because the skill appears to assume a specific language without opt-in or justification.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The example tells users to place an access key into an environment variable but gives no guidance on protecting that credential from shell history exposure, shared terminals, logs, or accidental disclosure in screenshots and transcripts. In an API-driven media generation skill, compromise of the key could let an attacker consume credits, access account-scoped resources, or impersonate the user against the LibTV service.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This markdown file instructs the user to set `LIBTV_ACCESS_KEY`, which is a sensitive credential, but it does not include any warning about keeping the key secret, avoiding shell history leakage, or not committing it to files. Under the markdown criteria for SQP-2, credential-related behavior should be accompanied by a warning when it could affect user privacy or security.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The example instructs users to export an access key, upload a local file, poll a remote service, and download results without any explicit warning that local content will be transmitted to a third-party platform and that credentials must be handled carefully. In a media-generation skill, these operations are expected, but the lack of disclosure can still lead users to unknowingly expose sensitive images, metadata, or tokens in shell history, logs, or untrusted environments.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The example instructs users to export an access key and perform local file upload/download operations, but it does not warn about protecting credentials, validating destination paths, or avoiding upload of sensitive local media. In a skill intended for broad automation use, this can lead to accidental secret exposure in shell history/logs and unintended transfer or storage of private files.

Natural-Language Policy Violations

Medium
Confidence
78% confidence
Finding
The module docstring and other user-visible error messages in this file are written only in Chinese, which imposes a specific language choice without indicating user selection or locale scoping. Under the stated policy, forcing a language without opt-in is a natural-language policy concern.

Natural-Language Policy Violations

Medium
Confidence
83% confidence
Finding
The strings "用户中断" and "请设置 LIBTV_ACCESS_KEY 环境变量" are user-visible outputs and are hardcoded only in Chinese. This creates a language/locale restriction without presenting an opt-in or documented justification.

Description-Behavior Mismatch

Medium
Confidence
83% confidence
Finding
The manifest focuses on remote LibTV media generation/editing, polling, templates, export, session history, and project management within the service. This module additionally creates and updates a local file in the user's home directory (`~/.libtv_projects.json`) to track project metadata, which is a behavioral expansion beyond the described remote tool operations and is not mentioned in the module's top-level description.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The file’s description and all user-facing CLI strings are written only in Chinese, which effectively fixes the interaction language for users of the skill. Under the policy, locale or language constraints should either be optional for the user or clearly documented as a justified region-specific limitation.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This file contains natural-language instructions and descriptions exclusively in Chinese in both the module docstring and CLI help text. Under the policy, forcing a specific language without offering a choice is a natural-language policy violation unless the locale restriction is explicitly documented and justified, which is not present here.

Static analysis

No suspicious patterns detected.