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.
