Back to skill

Security audit

AI Data Visualizer

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but dashboards generated from untrusted data can run injected browser code and always load external JavaScript, so users should review it before installing.

Install only if you are comfortable treating generated dashboards as active web pages. Avoid using this skill on untrusted CSV/JSON files or hosting generated dashboards on authenticated or sensitive domains until the HTML escaping and JSON embedding are fixed; consider bundling Chart.js locally or adding SRI and a clear network disclosure.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_dashboard.py:396
Finding
Stored Cross-Site Scripting in Generated Dashboards<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_dashboard.py`, lines 396, 480, 525–527, 540, 548–553, and 601–606 **Vulnerability Type**: Stored cross-site scripting through unescaped HTML and unsafe inline JSON embedding **Risk Level**: High ### Vulnerable Code ```python <title>{title}</title> ``` ```html <header> <h1>📊 {title}</h1> <button class="theme-toggle" onclick="toggleTheme()">🌓 Toggle Theme</button> </header> ``` ```javascript grid.innerHTML = stats.map(s => ` <div class="stat-card"> <div class="label">${s.label}</div> <div class="value">${s.value}</div> </div> `).join(''); ``` ```javascript card.innerHTML = `<h3>${c.title}</h3><div class="chart-wrap"><canvas id="chart${i}"></canvas></div>`; ``` ```javascript let html = '<thead><tr>' + TABLE_HEADERS.map(h => `<th>${h}</th>`).join('') + '</tr></thead>'; html += '<tbody>' + TABLE_ROWS.map(r => '<tr>' + TABLE_HEADERS.map(h => `<td>${r[h] || ''}</td>`).join('') + '</tr>' ).join('') + '</tbody>'; table.innerHTML = html; ``` ```python chart_data_js = json.dumps({'charts': chart_data_list, 'stats': stats}, ensure_ascii=False) headers_js = json.dumps(headers, ensure_ascii=False) table_rows_js = json.dumps(table_rows, ensure_ascii=False) html = HTML_TEMPLATE.format( title=title, chart_data=chart_data_js, headers=headers_js, table_rows=table_rows_js ) ``` ### Technical Analysis CSV and JSON headers, cell values, chart titles derived from headers, and the command-line `--title` value can all contain attacker-controlled text. These values are embedded in the generated dashboard without context-appropriate encoding. There are two distinct injection mechanisms: 1. The dashboard title is inserted directly into the HTML `<title>` and `<h1>` contexts through `str.format()` without HTML escaping. 2. Headers, raw table values, statistics labels, and chart titles are passed to JavaScript and subsequently rendered through `innerHTML`. Although `json ...[truncated 2064 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace HTML-string construction with safe DOM operations: - Create elements using `document.createElement()`. - Assign untrusted values through `textContent`. - Append elements using `appendChild()` or `replaceChildren()`. - Do not pass headers, cell values, chart titles, or statistics labels to `innerHTML`. 2. Escape the dashboard title before inserting it into HTML: ```python import html safe_title = html.escape(title, quote=True) ``` Use `safe_title` for both the `<title>` and `<h1>` contexts. 3. Safely embed serialized JSON. At minimum, encode characters that are significant to HTML parsing: ```python def safe_json_for_html(value): return ( json.dumps(value, ensure_ascii=False) .replace('&', r'\u0026') .replace('<', r'\u003c') .replace('>', r'\u003e') .replace('\u2028', r'\u2028') .replace('\u2029', r'\u2029') ) ``` A stronger design is to place JSON in a non-executable element such as `<script type="application/json">`, safely encode it for HTML, and parse the element's `textContent`. 4. Add a restrictive Content Security Policy. Prefer moving inline JavaScript to a separate local script so that `script-src` does not require `'unsafe-inline'`. 5. Add regression tests covering malicious input in titles, headers, JSON property names, and cells. Test payload classes should include: - HTML tags. - Event-handler attributes. - Quotes and template-expression characters. - Closing `</script>` sequences. - Encoded and mixed-case variants. 6. Treat every input dataset as untrusted, even when the dashboard is intended for local use. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/generate_dashboard.py:397
Finding
Remote Chart.js Dependency Loaded Without Subresource Integrity<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_dashboard.py`, line 397 **Vulnerability Type**: Unverified third-party JavaScript dependency **Risk Level**: Medium ### Vulnerable Code ```html <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script> ``` ### Technical Analysis Every generated dashboard loads and executes Chart.js from a third-party CDN when the dashboard is opened. The dependency version is pinned, which reduces accidental version drift, but the script element does not include a Subresource Integrity hash. Without integrity verification, the browser trusts whatever JavaScript is returned by the remote source. If the CDN, package distribution path, DNS resolution, or upstream release asset is compromised, modified JavaScript can execute within the dashboard's browser origin. The dependency is disclosed in the Skill documentation, and no evidence indicates that the current Chart.js URL is intentionally malicious. The risk arises from the absence of cryptographic verification and the dashboard's runtime reliance on an external executable resource. ### Attack Path 1. A user generates a dashboard with the script. 2. The user opens the dashboard while network access is available. 3. The browser requests Chart.js from the configured jsDelivr URL. 4. An attacker who has compromised the relevant supply-chain or delivery infrastructure causes a modified response to be returned. 5. Because no integrity hash is specified, the browser accepts and executes the modified JavaScript. 6. The malicious dependency runs with the same browser privileges and origin access as other dashboard scripts. ### Impact Assessment A successfully compromised dependency can execute arbitrary JavaScript in every affected dashboard when it is opened. It could: - Read and alter dashboard data and visualizations. - Access browser storage associated with the dashboard origin. - Send data to attacker-controlled services ...[truncated 300 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer bundling a reviewed Chart.js distribution file with the Skill so generated dashboards do not retrieve executable code at viewing time. 2. If CDN delivery is retained, add a verified Subresource Integrity hash and anonymous CORS mode: ```html <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js" integrity="sha384-VERIFIED_HASH_FOR_THE_EXACT_FILE" crossorigin="anonymous"></script> ``` The hash must be calculated or obtained from a trusted source for the exact referenced file; a placeholder must never be deployed. 3. Continue pinning the dependency to an exact version and review the package before upgrades. 4. Apply a Content Security Policy that limits scripts to the approved local or CDN source and blocks unexpected script origins. 5. Document whether generated dashboards require network access and provide an offline mode using the bundled dependency. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding
The code largely matches the declared purpose: it analyzes data characteristics, selects chart types, supports CSV and JSON input, produces interactive HTML dashboards, and includes a dark/light theme toggle. However, there are notable description-to-behavior gaps. First, while the description claims data statistical summaries, the implemented dashboard only shows very basic stats (rows, columns, numeric column count, and sometimes average of the first numeric column); the more complete compute_summary function is never used. Second, the CLI advertises a default theme option and the description suggests theme support, but the provided theme argument is never injected into the HTML output, so the default theme behavior is not actually implemented as expected. These are material implementation mismatches, though the overall primary purpose remains aligned.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill instructs writing files and generating output HTML but declares no explicit tool scope or permissions boundary. In an agent environment, this can cause the skill to receive broader-than-necessary filesystem capabilities, increasing the chance of unintended writes, overwrites, or misuse when handling untrusted user data.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The skill says it applies when a user mentions phrases like "generate chart", "plot", or "chart", which are broad everyday requests that can match many contexts beyond this specific CSV/JSON dashboard skill. The description does not provide tighter constraints or negative examples to clarify when the skill should or should not activate.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill explicitly tells the agent to write user-provided data to a temporary CSV file without warning about local persistence, retention, cleanup, or sensitivity handling. If the input contains confidential data, this can leave recoverable artifacts on disk, expose data to other local processes, or violate user expectations and privacy requirements.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The generated dashboard unconditionally loads Chart.js from a third-party CDN, which causes local dashboard viewing to make external network requests. This creates privacy and supply-chain risk: sensitive dataset context, access timing, IP metadata, and dependency integrity are exposed to an external service despite the skill presenting itself as a local visualization tool.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
Opening the generated HTML silently fetches Chart.js from a remote CDN with no user-facing disclosure. In the context of a data visualization skill that may process private CSV/JSON files, this hidden outbound connection is risky because users may assume the output is self-contained and safe to open offline.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The default title is hardcoded as Chinese text ("数据可视化仪表板"), which imposes a specific language by default. The file does not mention user language selection or justify a Chinese-only locale, so this is a natural-language locale policy concern.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The generated HTML depends on Chart.js from a CDN, but the skill does not warn users that opening the dashboard may require network access and can disclose metadata such as IP address, user agent, and access timing to a third party. This is especially relevant when dashboards contain sensitive business or personal datasets and are expected to remain fully local.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The template sets `<html lang="en">`, which forces a specific locale in the generated output without asking the user for language preference. The skill otherwise appears general-purpose rather than region-specific, so this is a natural-language locale policy concern.

Intent-Code Divergence

Low
Confidence
93% confidence
Finding
The function signature and CLI suggest a caller can choose a default light or dark theme, but the HTML always initializes from localStorage with a hardcoded fallback of 'light' and never uses the passed theme value. This is an intent-code divergence because the interface advertises behavior that the implementation does not honor.

Static analysis

No suspicious patterns detected.