Back to skill

Security audit

Agent Topology Visualizer

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent diagram generator, but crafted topology files can inject JavaScript into the generated HTML.

Use this only with topology JSON that you wrote or trust. Do not open or publish generated HTML from third-party topology files until the generator validates IDs, colors, counts, fonts, and replaces innerHTML/raw JavaScript interpolation with safe serialization and textContent-style rendering.

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.py:208
Finding
Stored Cross-Site Scripting Through Unsafe Topology Data Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.py`, lines 208, 224-257, 284-285, 348, 455, and 564-575 **Vulnerability Type**: Stored cross-site scripting and unsafe HTML, SVG, CSS, and JavaScript generation **Risk Level**: High ### Vulnerable Code #### Unescaped identifiers in SVG attributes — line 208 ```python lines.append(f'<path class="{cls}" data-conn="{i}" data-src="{src_id}" data-tgt="{tgt_id}" d="{d}"/>') ``` #### Unescaped node IDs in generated SVG elements — lines 224-261 ```python if ntype == "orchestrator": return f'''<g data-nid="{nid}" data-name="{name}" data-desc="{subtitle}" class="orch-group" style="cursor:pointer"> <circle class="pulse-ring" cx="{x}" cy="{y}" r="{r}"/> <circle class="pulse-ring pr2" cx="{x}" cy="{y}" r="{r}"/> <circle class="pulse-ring pr3" cx="{x}" cy="{y}" r="{r}"/> <circle class="orch-outer" cx="{x}" cy="{y}" r="{r}"/> <circle class="orch-inner" cx="{x}" cy="{y}" r="{int(r*0.72)}"/> <text class="lbl-emoji" x="{x}" y="{y-13}" font-size="22">{emoji}</text> <text class="lbl-orch-name" x="{x}" y="{y+10}">{name}</text> <text class="lbl-orch-sub" x="{x}" y="{y+25}">{subtitle}</text> </g>''' elif ntype == "system": return f'''<g class="sys-node" data-nid="{nid}" data-name="{name}" data-desc="{subtitle}" transform="translate({x:.0f},{y:.0f})"> <circle class="sys-circle" r="{r}"/> <text class="lbl-emoji" font-size="17" y="-10">{emoji}</text> <text class="lbl-main" y="8">{name}</text> <text class="lbl-sub" y="20">{subtitle}</text> </g>''' elif ntype == "agent": return f'''<g class="agent-node" data-nid="{nid}" data-name="{name}" data-desc="{subtitle}" transform="translate({x:.0f},{y:.0f})"> <circle class="agent-circle" r="{r}"/> <text class="lbl-emoji" font-size="13" y="-7">{emoji}</text> <text class="lbl-main" font-size="10" y="7">{name}</text> <text class="lbl-sub" y="19">{subtitle}</text> </g>''' elif ntype == "human": re ...[truncated 6070 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Eliminate the `innerHTML` sink.** Construct the tooltip with DOM nodes and `textContent`: ```javascript function showTip(e, n, d) { clearTimeout(hideTimer); tip.replaceChildren(); var strong = document.createElement('strong'); strong.textContent = n; tip.appendChild(strong); if (d) { tip.appendChild(document.createTextNode(' — ' + d)); } tip.classList.add('show'); moveTip(e); } ``` 2. **Strictly validate node IDs and connection endpoints.** Enforce the documented identifier syntax and reject duplicate IDs: ```python ID_PATTERN = re.compile(r"^[a-z0-9_]+$") if not isinstance(n["id"], str) or not ID_PATTERN.fullmatch(n["id"]): raise ValueError("Node IDs must contain only lowercase letters, digits, and underscores") if n["id"] in node_ids: raise ValueError(f"Duplicate node ID: {n['id']}") ``` 3. **Apply context-aware HTML attribute encoding.** Every value inserted into an HTML or SVG attribute must use: ```python html_mod.escape(str(value), quote=True) ``` This must include node IDs, connection source and target IDs, titles, names, descriptions, and any other string-valued attributes. 4. **Validate numeric fields before generation.** Require `viewBox`, positions, radii, and `starCount` to be finite numeric values. Apply safe bounds, such as a nonnegative `starCount` with a reasonable maximum, to prevent script injection and resource exhaustion. 5. **Serialize all JavaScript values with `json.dumps`.** Never interpolate raw strings into JavaScript literals: ```python count = validate_star_count(bg_opts.get("starCount", 150)) color_js = json.dumps(validate_color(bg_opts.get("starColor", "rgba(200,214,224,0.3)"))) ``` 6. **Allowlist CSS and SVG values.** Parse color fields as supported hexadecimal, RGB, or RGBA formats. Reject values containing delimiters or unsupported CSS const ...[truncated 665 chars]
Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (3)

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill instructs users to run a local generator that writes output files such as diagram.html and diagram.svg, but the skill metadata declares no explicit tool scope or permissions. This creates an authorization and transparency gap: an agent may perform file-write actions without an allowlisted declaration, making it harder to enforce least privilege and easier for future changes to expand write behavior unnoticed.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
This is a real DOM XSS issue in the generated output, even though the finding is anchored near the CSS sanitizer. Topology data is inserted into SVG data-* attributes without escaping the node id, then later read by JavaScript and rendered with tip.innerHTML; additionally, untrusted fields such as background.starColor are interpolated directly into inline <script> strings, making quote-breaking script injection possible when the generated HTML is opened.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The generated HTML hard-codes `<html lang="en">`, which imposes a specific language/locale choice on all outputs. This is a natural-language policy concern because the file does not offer a user-selectable locale or explain why English is required.