Back to skill

Security audit

svg-composer

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly does what it says, but its preview generator can write outside the chosen folder and create unsafe local HTML if given untrusted input.

Install only if you will use it on trusted inputs and controlled output folders. Avoid passing untrusted svg_list filenames, text, or SVG content to preview generation, do not share generated preview HTML that contains local file links, and consider pinning svgpathtools before use.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/svg_composer.py:858
Finding
Arbitrary File Write Through Unsanitized Preview Filenames<![CDATA[ ## Vulnerability Details **File Location**: `scripts/svg_composer.py:858-863` **Vulnerability Type**: Path traversal and arbitrary file write **Risk Level**: High ### Vulnerable Code ```python # 保存 SVG 文件 saved_files = [] for label, filename, svg_content in svg_list: filepath = os.path.join(output_dir, filename) with open(filepath, 'w', encoding='utf-8') as f: f.write(svg_content) saved_files.append((label, filename, filepath)) ``` ### Technical Analysis The public `generate_preview_html()` function accepts an `svg_list` containing caller-controlled filenames and content. Each filename is passed directly to `os.path.join()` and then to `open()` in write mode. `os.path.join()` does not enforce directory confinement: - A filename containing `../` can traverse outside `output_dir`. - An absolute filename can cause the supplied `output_dir` to be discarded. - Existing files are silently truncated and overwritten by `"w"` mode. There is no basename restriction, canonical path validation, extension enforcement, or check that the resolved destination remains under the intended output directory. ### Attack Path 1. An attacker obtains control over, or influences, the `svg_list` argument passed to `generate_preview_html()`. 2. The attacker supplies an entry such as: ```python ( "horizontal", "../../attacker-controlled.svg", "<svg xmlns='http://www.w3.org/2000/svg'></svg>" ) ``` 3. The function joins the malicious filename with `output_dir`. 4. The resulting path resolves outside the designated output directory. 5. The function opens that path in write mode and writes attacker-controlled content. 6. If the destination already exists, it is overwritten. ### Impact Assessment An attacker can create or overwrite files anywhere writable by the Python process. The exact scope is limited to the operating-system privileges of the process running the Skill; the code does not independently elevate privilege ...[truncated 403 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject absolute filenames and any filename containing path separators or traversal components. 2. Accept only a basename generated or validated by the application: ```python from pathlib import Path output_root = Path(output_dir).resolve() safe_name = Path(filename).name if safe_name != filename or not safe_name.lower().endswith(".svg"): raise ValueError("Invalid SVG filename") destination = (output_root / safe_name).resolve() if output_root not in destination.parents: raise ValueError("Destination escapes output directory") ``` 3. Prefer generating filenames internally instead of accepting them from callers. 4. Use exclusive creation mode (`"x"`) when overwriting is not explicitly required. 5. Apply file-count and output-size limits. 6. If overwriting is required, explicitly authorize the destination and use an atomic temporary-file replacement strategy. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/svg_composer.py:870
Finding
Path Traversal and Stored HTML Injection Through Unsanitized Preview Text<![CDATA[ ## Vulnerability Details **File Location**: `scripts/svg_composer.py:870, 909, 965, 998-1002` **Vulnerability Type**: Stored HTML injection and path traversal **Risk Level**: High ### Vulnerable Code ```python # 获取文件夹路径 folder_path = output_dir.replace('\\', '/') # 生成 HTML title = f"{text} SVG 预览" if text else "SVG 预览" direction_label = "横向" if direction == 'horizontal' else "纵向" ``` ```python html_content = f'''<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{title}</title> ``` ```python <body> <h1>{title}</h1> ``` ```python # 保存 HTML html_filename = f"{text}_preview.html" if text else "preview.html" html_filename = html_filename.replace(' ', '_') html_filepath = os.path.join(output_dir, html_filename) with open(html_filepath, 'w', encoding='utf-8') as f: f.write(html_content) ``` ### Technical Analysis The caller-controlled `text` value is used in two security-sensitive contexts without context-specific validation: 1. It is inserted directly into the HTML `<title>` and `<h1>` elements without HTML escaping. 2. It is incorporated into `html_filename` and written using `os.path.join()` without path confinement. Replacing spaces with underscores does not neutralize HTML metacharacters, absolute paths, directory separators, or `../` traversal sequences. Although `compose_text()` restricts supported characters when it is invoked, `generate_preview_html()` can be called with both `svg_list` and `text`. In that execution path, `text` is not passed through character-set validation before reaching the HTML and filesystem sinks. The generated preview also embeds `svg_content` directly into the HTML: ```python preview_items_html += f''' <div class="preview-item"> <h3>{'横向' if label == 'horizontal' else '纵向'}</h3> <div class="svg-wrapper"> {svg_content} </div> </div> ''' ``` Therefore, untrusted custom `svg_list` cont ...[truncated 1663 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape all values inserted into HTML text or attributes: ```python import html safe_title = html.escape(str(title), quote=True) safe_filename_label = html.escape(str(filename), quote=True) ``` 2. Do not construct preview filenames from untrusted display text. Use a fixed filename or an internally generated identifier, for example `preview.html`. 3. Resolve the output path and enforce containment under `output_dir`. 4. Reject absolute paths, path separators, null bytes, and traversal components in all caller-supplied filenames. 5. Treat caller-provided SVG as active content. Sanitize it with an SVG-aware allowlist or render it in an isolated context. 6. Consider displaying SVG files through `<img src="...">` rather than embedding arbitrary SVG markup directly into the HTML DOM. 7. Apply a restrictive Content Security Policy, such as disallowing scripts and remote connections, as defense in depth. 8. Use separate variables for display labels, filenames, URL attributes, and raw SVG; validate or encode each according to its output context. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/svg_composer.py:489
Finding
Unbounded Combinatorial Generation Allows Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/svg_composer.py:489-493, 548-553, 655-666` **Vulnerability Type**: Algorithmic complexity and resource-exhaustion denial of service **Risk Level**: Medium ### Vulnerable Code ```python # 全排列 result = [] for perm in itertools.permutations(unique_chars): perm_str = ''.join(perm) svg = compose_text(perm_str, direction, canvas_size, margin, align, fill, font_height_ratio, charset) result.append(svg) return result ``` ```python # 笛卡尔积 result = [] for combo in itertools.product(unique_chars, repeat=length): combo_str = ''.join(combo) svg = compose_text(combo_str, direction, canvas_size, margin, align, fill, font_height_ratio, charset) result.append(svg) return result ``` ```python if allow_repeat: comb_iter = itertools.product(symbols, repeat=n) else: comb_iter = itertools.permutations(symbols, n) combinations = [''.join(combo) for combo in comb_iter] if not combinations: print(f"警告:当 n={n} 且不允许重复时,没有有效组合。") return print(f"将生成 {len(combinations)} 个组合: {combinations}") ``` ### Technical Analysis The generation functions accept user-influenced input sizes and combination lengths without enforcing an upper bound or output budget. The number of outputs grows rapidly: - `compose_permutations()` produces `k!` outputs for `k` unique characters. - `compose_combinations()` produces `k^length` outputs. - `batch_compose()` produces either `k^n` repeated combinations or `k!/(k-n)!` permutations. Every generated SVG is accumulated in a Python list. `batch_compose()` additionally materializes all combination names before processing them, logs the complete list, and can write every result to disk. Each output invokes SVG composition work, increasing both CPU and memory costs. Consequently, moderately large values can consume excessive CPU time, memory, log storage, and disk space. ### Attack Path 1. An attacker reaches an ...[truncated 1246 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Calculate the projected result count before beginning generation: ```python projected = len(unique_chars) ** length if projected > MAX_OUTPUTS: raise ValueError("Requested combination count exceeds the configured limit") ``` 2. Use `math.factorial()` or permutation-count calculations to validate permutation requests before iteration. 3. Enforce strict limits on: - Input character count. - Combination or permutation length. - Total output count. - Per-output and aggregate byte size. - Execution time and disk use. 4. Return or consume generators incrementally instead of storing all SVG documents in a list. 5. Avoid materializing `combinations` in `batch_compose()`; iterate over `comb_iter` directly. 6. Do not print complete combination lists. Log only bounded summaries and counts. 7. Run externally submitted generation jobs with memory, CPU, file-count, and disk quotas. 8. Support cancellation and deadlines for long-running batch operations. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:350
Finding
Unpinned Third-Party Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:350-353` **Vulnerability Type**: Unpinned dependency and supply-chain risk **Risk Level**: Low ### Vulnerable Code ```markdown ## 依赖 - Python 3.7+ - svgpathtools (`pip install svgpathtools`) ``` ### Technical Analysis The installation documentation directs users to install `svgpathtools` without a version constraint or integrity hash. The resolved artifact can therefore change over time, and separate installations of the same Skill may execute different dependency code. This is not evidence that the named package is currently malicious. The risk arises from trusting a mutable latest release without a reviewed version, locked transitive dependencies, or artifact integrity verification. Python package installation can execute package build logic, while imported dependency code executes with the privileges of the Python process. A future compromised, malicious, or incompatible release could therefore affect installation or runtime behavior. ### Attack Path 1. A user follows the documented command: ```bash pip install svgpathtools ``` 2. The package index resolves whichever release is current at installation time. 3. The installer downloads the package and its transitive dependencies without project-specified hashes. 4. If the resolved release or dependency has been compromised, malicious installation or import-time code executes. 5. That code runs with the privileges available to the user or environment performing the installation. ### Impact Assessment The potential scope equals the privileges of the environment installing or importing the dependency. In a compromised-release scenario, consequences could include arbitrary code execution, access to project data and environment variables, modification of writable files, and network access. The practical likelihood is lower than the direct code flaws because exploitation depends on a malicious or compromised dependency release or d ...[truncated 26 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin an audited `svgpathtools` version in a dependency manifest. 2. Record cryptographic hashes using a hash-locked requirements file, for example with `pip-compile --generate-hashes`. 3. Pin and review relevant transitive dependencies. 4. Install with hash enforcement: ```bash pip install --require-hashes -r requirements.txt ``` 5. Use a trusted or internally mirrored package index. 6. Add automated dependency vulnerability and provenance scanning. 7. Update dependencies through a controlled review process rather than automatically resolving the newest release. ]]>
Vulnerability Patterns
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (8)

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The docstring says svg_list elements are '(名称, svg字符串)', i.e. 2-tuples, but the implementation iterates 'for label, filename, svg_content in svg_list', requiring 3-tuples. This is an active contradiction in the inline documentation and can mislead developers into passing the wrong structure, causing runtime errors or incorrect use of the preview feature.

Intent-Code Divergence

Medium
Confidence
92% confidence
Finding
The docstring says the function is equivalent to loading a symbol directory with 'load_symbols(symbol_dir)', but the actual parameter is 'symbol_files', a dict mapping each character to a specific SVG file path, which the code reads one by one. This is a direct intent/documentation mismatch rather than merely omitted detail.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The documentation explicitly promotes bulk generation of SVGs and creation of preview HTML containing download links and file:/// folder links, but it does not warn users about disk usage, accidental large-output generation, or local path exposure when the preview HTML is shared or opened in sensitive environments. While this is not an exploit by itself, it can lead to unintended local information disclosure and resource consumption, especially when combined with combinatorial modes that may generate very large numbers of files.

Natural-Language Policy Violations

Low
Confidence
86% confidence
Finding
This code file contains natural-language content entirely in Chinese in the module docstring and comments, which imposes a specific language/locale on readers and maintainers without any opt-in or justification. Under the policy, language-specific constraints should either offer a choice or be clearly documented as region-specific.

Intent-Code Divergence

Low
Confidence
94% confidence
Finding
The module docstring and data section present this file as built-in charset data for supported characters, but `SUPPORTED_SYMBOLS` lists additional symbols such as `:`, `` ` ``, `{`, `|`, `}`, and `~` that do not appear as keys in `SVG_ALPHABET`. `ALL_SUPPORTED` is then derived from these support lists, so the code advertises support beyond what the actual glyph dataset contains.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This code creates the output directory and later writes multiple SVG files into it, which is a safety-relevant file-write operation. While there are progress print statements, there is no user-facing warning or confirmation that existing files in the target directory may be created or overwritten as part of batch generation.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The preview generator writes attacker-influenced SVG/HTML files to disk and builds clickable local file:// links, which can make unsafe content easier to open in a browser. In this skill context, SVG content may include active or browser-sensitive constructs if sourced externally, so generating and encouraging interaction with local files expands the attack surface to stored client-side content execution or local-file phishing/trust abuse.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The HTML template sets `<html lang="zh">`, which forces a specific locale in generated output. There is no option for callers to choose the language or locale, and the function can be used with arbitrary text rather than a clearly China-specific compliance context.

Static analysis

No suspicious patterns detected.