Back to skill

Security audit

SVG Artist

Security checks for vulnerabilities and agentic risk

Overview

This SVG skill is mostly aligned with its stated purpose, but its helper script can turn a crafted output filename into arbitrary shell command execution.

Review before installing. Avoid running artifact/scripts/generate_svg.js with filenames or paths supplied by untrusted users, prompts, or external content. The helper should be fixed to validate output filenames, confine outputs to a dedicated directory, and use execFileSync or spawnSync with argument arrays instead of shell command strings.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_svg.js:140
Finding

OS Command Injection Through Attacker-Controlled Output Path

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_svg.js:140-147
Input Sources: scripts/generate_svg.js:21-31 and scripts/generate_svg.js:113-116
Vulnerability Type: OS command injection
Risk Level: High

The output path is populated from command-line arguments without validation:

js
for (let i = 0; i < args.length; i++) {
  if (args[i] === '--template' || args[i] === '-t') {
    template = args[++i];
  } else if (args[i] === '--output' || args[i] === '-o') {
    outputPath = args[++i];
  } else if (!args[i].startsWith('-')) {
    if (!description) {
      description = args[i];
    } else {
      outputPath = args[i];
    }
  }
}

Template mode may also select the final command-line argument directly:

js
if (template && templates[template]) {
  const svg = templates[template](400);
  // Use outputPath from args if provided
  const finalOutput = args[args.length - 1].endsWith('.png') ? args[args.length - 1] : outputPath;
  saveAndConvert(svg, finalOutput);
  console.log(`Template '${template}' saved to ${finalOutput}`);
  process.exit(0);
}

The vulnerable command execution segment is:

js
function saveAndConvert(svg, output) {
  const svgPath = output.replace('.png', '.svg');
  fs.writeFileSync(svgPath, svg);

  try {
    execSync(`rsvg-convert "${svgPath}" -o "${output}"`, { stdio: 'pipe' });
  } catch (e) {
    try {
      execSync(`convert "${svgPath}" "${output}"`, { stdio: 'pipe' });

Technical Analysis

Node.js execSync executes the supplied string through a system shell. Both svgPath and output are derived from an untrusted command-line argument and are interpolated directly into shell command strings.

Wrapping these values in double quotes does not prevent command injection. Shell constructs such as command substitution using $() or backticks remain active inside double-quoted strings. An ...[truncated 2153 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace shell-based execSync calls with execFileSync or spawnSync, passing each argument separately:

    js
    const path = require('path');
    const { execFileSync } = require('child_process');
    
    function saveAndConvert(svg, requestedOutput) {
      const outputDir = '/tmp/svg-artist';
      fs.mkdirSync(outputDir, { recursive: true, mode: 0o700 });
    
      const baseName = path.basename(requestedOutput);
      if (!/^[A-Za-z0-9._-]+\.png$/i.test(baseName)) {
        throw new Error('Invalid output filename');
      }
    
      const output = path.join(outputDir, baseName);
      const svgPath = path.join(
        outputDir,
        `${path.basename(baseName, path.extname(baseName))}.svg`
      );
    
      fs.writeFileSync(svgPath, svg, {
        encoding: 'utf8',
        mode: 0o600,
        flag: 'wx'
      });
    
      try {
        execFileSync('rsvg-convert', [svgPath, '-o', output], {
          stdio: 'pipe',
          shell: false
        });
      } catch (error) {
        execFileSync('convert', [svgPath, output], {
          stdio: 'pipe',
          shell: false
        });
      }
    }
    
  2. Resolve output files beneath a dedicated, application-controlled directory. Reject absolute paths, traversal sequences, path separators, and any resolved path outside that directory.

  3. Require an explicit .png extension rather than using String.replace('.png', '.svg'). Construct the SVG filename using path.parse or path.basename.

  4. Create temporary files with restrictive permissions and collision-resistant names. Use exclusive creation where appropriate to reduce overwrite and symlink risks.

  5. Run conversion utilities with the least privileges required and a minimal environment. Apply execution timeouts and resource limits to reduce the impact of malformed inputs.

  6. Add automated security tests covering $(), backticks, embedded quotes, semicolons, newlines, abs ...[truncated 42 chars]

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The declared description says the skill uses an LLM to write SVG code directly from user text requests. However, this code does not implement that behavior. Outside of selecting one of several predefined templates, it ignores the description as input for image generation and merely prints it. Its main implemented functionality is template emission plus file output and SVG-to-PNG conversion via shell commands. That is a materially different primary behavior from the declared text-to-SVG generation capability, so this is a description/behavior mismatch.

Content

No source excerpt is available for this finding.

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 31)May include surrounding context.

svg
<svg width='400' height='400' xmlns='http://www.w3.org/2000/svg'>
  <!-- Background -->
  <rect width='400' height='400' fill='#87CEEB'/>
  <!-- Your shapes here -->
</svg>

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The top-level comment frames the script as a template helper and states that the LLM should write SVG code directly for best results. In practice, the script's main concrete behavior is saving SVG to disk and invoking conversion tools to produce PNG output, which materially differs from the documented direct-SVG framing.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest describes a skill that generates SVG images by having the LLM write SVG directly, avoiding image-generation APIs. This helper instead executes external system programs (rsvg-convert and convert) to render PNGs, which is a separate subprocess capability not obviously required by the stated purpose of direct SVG generation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The skill instructs the agent to write files under /tmp and execute external conversion binaries without warning or explicit consent boundaries. In an agent environment, this can expand the action surface from harmless text generation into local filesystem modification and tool execution, which may violate least-privilege expectations or surprise users.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script writes an SVG file to disk and invokes external conversion commands via execSync, which are safety-relevant operations for a code file. Although usage comments describe the script's purpose, there is no explicit warning or confirmation when these operations execute, and the runtime output does not disclose the file write/conversion before it happens.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/generate_svg.js:138