Back to skill

Security audit

Text to Image

Security checks for vulnerabilities and agentic risk

Overview

This text-to-image skill is coherent, but it needs review because it can write files to arbitrary paths and lacks safeguards around SVG generation and large render requests.

Install only in an environment that can restrict filesystem writes and resource usage. Prefer tmp/workspace-only output paths, avoid processing untrusted specs as SVG, disable data URIs for large renders, and set external limits for image dimensions and input size until the skill validates these itself.

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/render_text_image.py:480
Finding

SVG Attribute Injection Through Unvalidated Style Fields

Content
View full analysis
', f'', ] for index, line in enumerate(lines): y = start_y + index * font_size * line_height current_x = line_start_x(align, width, padding, line_pixel_width(line, font_size)) svg_lines.append( f'' ) for item in line: color = escape(str(item["color"])) text = escape(item["text"]) svg_lines.append( f'{text}' ) current_x += text_width(item["text"], font_size) svg_lines.append("") ``` ### Technical Analysis The `background`, `font_family`, and segment or highlight `color` fields are controlled by the input specification and are interpolated into double-quoted SVG attributes. The code applies `xml.sax.saxutils.escape()` without providing an entity mapping for quotation marks. By default, this function escapes `&`, `<`, and `>`, but it does not escape `"`. An attacker can therefore insert a double quote to terminate the intended attribute and inject additional SVG attributes ...[truncated 2084 chars]
Remediation
View remediation
str: return escape( str(value), { '"': """, "'": "'", }, ) ``` 4. Prefer constructing the SVG with a standards-compliant XML library that serializes attribute values safely instead of assembling XML through string interpolation. 5. Consider sanitizing the completed SVG with a policy that rejects scripts, event-handler attributes, external references, foreign objects, and unsafe URL schemes. 6. Add regression tests containing quotation marks and attempted SVG payloads in every attribute-bearing input field. Verify that the resulting SVG parses only as the intended elements and attributes. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/render_text_image.py:515
Finding

Unbounded Rendering Parameters Permit Resource Exhaustion

Content
View full analysis
Tuple[bytes, float, List[List[Dict[str, Any]]]]: width = int(spec["width"]) height = int(spec["height"]) padding = int(spec.get("padding", DEFAULT_PADDING)) line_height = float(spec.get("line_height", DEFAULT_LINE_HEIGHT)) align = spec.get("align", DEFAULT_ALIGN) valign = spec.get("valign", DEFAULT_VALIGN) background = parse_background(spec.get("background", DEFAULT_BACKGROUND), image_format) font_path = choose_font_file(spec) ``` The dimensions are then used directly for image allocation: ```python image_mode = "RGBA" if image_format == "png" else "RGB" background_color = background if image_mode == "RGBA" else background[:3] image = Image.new(image_mode, (width, height), background_color) draw = ImageDraw.Draw(image) ``` The only explicit dimension validation checks for field presence: ```python def ensure_required_dimensions(spec: Dict[str, Any]) -> None: if "width" not in spec or "height" not in spec: raise SystemExit("Spec must include integer 'width' and 'height'.") ``` ### Technical Analysis The renderer requires `width` and `height`, but it does not enforce that they are positive and within safe limits. It also does not constrain the total pixel count, input text length, number of segments, font size, line count, generated output size, or base64 response size. For raster output, `Image.new()` allocates memory based directly on the attacker-controlled dimensions. Large images may require several bytes per pixel before accounting for drawing state, encoded output buffers, and the base64 copy. The script stores the encoded image in an in-memory `BytesIO` object and may create another exp ...[truncated 1886 chars]
Remediation
View remediation
None: width = as_int(spec.get("width"), "width") height = as_int(spec.get("height"), "height") if width <= 0 or height <= 0: raise SystemExit("Width and height must be positive.") if width > MAX_WIDTH or height > MAX_HEIGHT: raise SystemExit("Requested dimensions exceed the supported limit.") if width * height > MAX_PIXELS: raise SystemExit("Requested image exceeds the maximum pixel count.") ``` 2. Define and enforce limits for: - Text length. - Number of segments and highlight ranges. - Minimum and maximum font sizes. - Padding and line-height values. - Maximum generated file size. - Maximum line count. - Maximum request and JSON size. 3. Omit data URIs by default, especially for raster output. Only create a base64 representation when explicitly requested and when the encoded output is below a safe size threshold. 4. Apply execution-level safeguards such as memory limits, CPU timeouts, per-request deadlines, output quotas, and concurrency controls. 5. Reject non-finite floating-point values and validate all numeric parameters before arithmetic or allocation. 6. Use streaming or bounded-output designs where practical, and delete temporary output according to a defined retention policy. 7. Add tests for negative, zero, excessively large, and boundary dimensions, as well as oversized text and segment arrays. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding

The skill describes file read/write behavior and explicitly writes output files, but it does not declare any tool scope restrictions such as permissions or allowed-tools. In an agent ecosystem, missing scope declarations can let a broadly-invoked skill operate with more filesystem capability than reviewers or orchestrators expect, increasing the chance of unintended file access or overwrites.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The description contains many broad trigger phrases such as generic requests for local image paths, temp file paths, and text/image conversion across multiple languages. Overly broad activation wording can cause the agent to select this skill for common prompts, expanding exposure to its file-writing behavior and making accidental or policy-bypassing invocation more likely.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script claims to return a temp file under the skill tmp folder, but the --output argument is passed directly into write_output_files() and written with parent directories auto-created and no restriction to SKILL_ROOT/tmp. In an agent setting, this enables arbitrary file write anywhere the process has permission, which can overwrite application files, drop payloads into sensitive locations, or tamper with other workspace content.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

This manifest/config file contains natural-language content in specific languages but provides no indication that language selection is user-driven or that the locale restriction is intentional. Under the language/locale policy rule, hard-coded language behavior can be a policy concern when no opt-in or justification is present.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The manifest discusses built-in font fallback and controllable font rendering, but does not state that the skill may inspect arbitrary user-supplied local font files. Allowing any existing path as font_path gives the skill extra filesystem access beyond the described built-in, cross-platform rendering behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.