Back to skill

Security audit

Bili Sunflower Publish

Security checks for vulnerabilities and agentic risk

Overview

This Bilibili publishing skill is mostly coherent, but it can automatically post publicly from an authenticated account and can inline local image files from overly broad paths into published content.

Review this skill carefully before installing. Use it only with Markdown or HTML files you trust, because referenced local images can be read from disk and embedded into the Bilibili post. Before publishing, manually verify the processed content, target, visibility, account, and any images; safer behavior would require path containment for images and an explicit final publish confirmation.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/preprocess_html.py:48
Finding
Arbitrary Local Image File Disclosure Through HTML Image References## Vulnerability Details **File Location**: `scripts/preprocess_html.py`, lines 48–62 **Vulnerability Type**: Arbitrary local file read and unintended data disclosure **Risk Level**: High ### Vulnerable Code ```python def inline_img(m): prefix = m.group(1) quote = m.group(2) src = m.group(3) if src.startswith(("http://", "https://", "data:")): return m.group(0) img_path = os.path.join(html_dir, src) if not os.path.isabs(src) else src if not os.path.isfile(img_path): return m.group(0) mime, _ = mimetypes.guess_type(img_path) if not mime or not mime.startswith("image/"): return m.group(0) with open(img_path, "rb") as img_f: b64 = base64.b64encode(img_f.read()).decode("ascii") return f'{prefix}{quote}data:{mime};base64,{b64}{quote}' ``` ### Technical Analysis The `src` value is extracted from document-controlled HTML and used to construct a local filesystem path. Absolute paths are explicitly accepted through `os.path.isabs(src)`, while relative paths are joined to the HTML directory without canonicalization or a containment check. Consequently, references containing `../` can escape the source document directory. The `os.path.isfile` check only confirms that the target exists as a file. The subsequent MIME check uses `mimetypes.guess_type`, which infers the type from the filename rather than validating the file contents. It therefore does not establish that accessing the file is authorized or that it is genuinely an image. The selected file is read with the agent process's filesystem permissions, encoded as a data URI, and inserted into the processed HTML. The workflow in `SKILL.md` subsequently directs the agent to inject this HTML into an authenticated Bilibili editor and publish it. This can convert a local file read into external disclosure. ### Attack Path 1. An attacker supplies or influences an HTML document processed by the Skill. 2. The attacker includes an image such as: ...[truncated 1120 chars]
Remediation
## Remediation Suggestions 1. Reject absolute image paths by default. 2. Resolve the document directory and candidate path with `os.path.realpath`. 3. Require the resolved candidate to remain inside the document directory or an explicitly approved asset root: ```python asset_root = os.path.realpath(html_dir) candidate = os.path.realpath(os.path.join(asset_root, src)) if os.path.commonpath([asset_root, candidate]) != asset_root: return m.group(0) ``` 4. Guard against symlink escapes by performing containment checks on resolved paths and opening files defensively. 5. Validate image contents using a trusted image parser or file-signature validation rather than relying on extensions. 6. Apply per-file and aggregate size limits before reading or encoding assets. 7. List all local files selected for embedding and require explicit user confirmation before publication. 8. Consider disabling local-file inlining unless the user expressly enables it.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/preprocess_md.py:53
Finding
Arbitrary Local Image File Disclosure Through Markdown Image References## Vulnerability Details **File Location**: `scripts/preprocess_md.py`, lines 53–67 **Vulnerability Type**: Arbitrary local file read and unintended data disclosure **Risk Level**: High ### Vulnerable Code ```python def inline_md_img(m): prefix = m.group(1) # ![alt]( src = m.group(2) # path suffix = m.group(3) # optional title + ) if src.startswith(("http://", "https://", "data:")): return m.group(0) img_path = os.path.join(md_dir, src) if not os.path.isabs(src) else src if not os.path.isfile(img_path): return m.group(0) mime, _ = mimetypes.guess_type(img_path) if not mime or not mime.startswith("image/"): return m.group(0) with open(img_path, "rb") as img_f: b64 = base64.b64encode(img_f.read()).decode("ascii") return f'{prefix}data:{mime};base64,{b64}{suffix}' ``` ### Technical Analysis The Markdown image path is controlled by the input document. The code permits absolute paths and resolves relative paths with `os.path.join` without ensuring that the final canonical path remains under the Markdown document directory. Directory traversal sequences can therefore select files outside the intended asset scope. The MIME restriction is based solely on `mimetypes.guess_type`, which derives a type from the path's extension. This is not an authorization boundary and does not validate actual file content. Any readable file with an image-associated extension can be loaded. The bytes are base64-encoded into the processed Markdown. According to `SKILL.md`, that Markdown is imported into the Bilibili editor using `window.editor.commands.importMarkdown` and then published. Thus, document-controlled file references can cause local data to be transmitted to a third-party service. ### Attack Path 1. An attacker provides or modifies a Markdown file intended for publication. 2. The file contains a reference such as: ```markdown ![asset](/home/user/private/photo.png) ``` or: ```mar ...[truncated 958 chars]
Remediation
## Remediation Suggestions 1. Disallow absolute Markdown image paths unless individually authorized. 2. Canonicalize the asset root and candidate path before access: ```python asset_root = os.path.realpath(md_dir) candidate = os.path.realpath(os.path.join(asset_root, src)) if os.path.commonpath([asset_root, candidate]) != asset_root: return m.group(0) ``` 3. Ensure resolved symlink targets remain within the approved asset root. 4. Validate image signatures and decode images with a trusted library instead of trusting filename-derived MIME types. 5. Enforce conservative file-size and total-output-size limits to avoid excessive reads and generated payloads. 6. Display the canonical path of every file to be embedded and obtain explicit confirmation before transmitting content. 7. Prefer a user-selected asset allowlist over automatically following arbitrary paths found in Markdown.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill is designed to proceed directly through navigation, content insertion, and clicking the final publish button, but it does not require an explicit confirmation immediately before the irreversible publish action. In context, this is especially dangerous because the automation targets a real authenticated Bilibili account and can post publicly or to a community with a single mistaken invocation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README states that local images are automatically inlined as base64 data URIs for upload, but does not warn users that local file contents will be embedded into the published post. This can unintentionally expose sensitive local images or metadata if users provide files assuming only text will be published.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README advertises 'one-click publish' and direct publishing behavior without clearly warning that content will be sent to an external third-party platform. In an agent skill context, minimizing friction around outbound publication increases the risk of accidental data exfiltration or unintended posting, especially if users do not realize the action is immediately public-facing.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill performs local file reads and writes via preprocessing scripts but does not declare any tool scope or allowed-tools boundaries. That creates an authorization gap where the runtime may grant broader filesystem access than users or reviewers expect, increasing the chance of unintended file access or misuse.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger language is broad enough to activate on many loosely related publishing requests, without exclusions or a requirement to confirm target, account, and content source. In a skill that culminates in posting content to a live social platform, ambiguous activation increases the risk of unintended execution and accidental publication.

Whitespace Padding

Medium
Category
Prompt Injection
Content
Both `tribee_id` and `tribee_name` are required for the publish URL. Resolve missing params:

| User provides | Resolution                                                                                                                                            |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| id + name     | Direct → publish URL                                                                                                                                  |
| id only       | Navigate to `https://www.bilibili.com/bubble/home/{id}`, extract tribee name from the page, then → publish URL                                        |
Confidence
70% confidence
Finding
Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Whitespace Padding

Medium
Category
Prompt Injection
Content
| User provides | Resolution                                                                                                                                            |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| id + name     | Direct → publish URL                                                                                                                                  |
| id only       | Navigate to `https://www.bilibili.com/bubble/home/{id}`, extract tribee name from the page, then → publish URL                                        |
| name only     | Search `https://search.bilibili.com/all?keyword={name}`, find the card linking to `bilibili.com/bubble/home/{id}`, extract `{id}`, then → publish URL |
Confidence
70% confidence
Finding
Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Static analysis

No suspicious patterns detected.