Back to skill

Security audit

d3-visualization

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent D3 visualization helper, but it ships and recommends tooltip code that can execute untrusted data as browser HTML.

Review this skill before installing. It is not evidence of theft or system compromise, but use it only with trusted data unless tooltip content is changed to use text nodes or a sanitizer. Also align the D3 version guidance before relying on deterministic builds, and avoid serving generated outputs from an authenticated or sensitive web origin until the tooltip rendering is fixed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/bubble_chart_example.js:104
Finding

DOM-based cross-site scripting in bubble chart tooltips

Content
View full analysis
{ // Parse numbers data.forEach(d => { d.value = d.marketCap ? +d.marketCap : null; d.ticker = d.ticker; d.sector = d.sector; }); createBubbleChart(data); }); ``` ```javascript function showTooltip(event, d) { const tooltip = d3.select('#tooltip'); tooltip .classed('visible', true) .html(` ${d.ticker}
${d.name || 'N/A'}
Sector: ${d.sector} `) .style('left', (event.pageX + 10) + 'px') .style('top', (event.pageY - 10) + 'px'); } ``` ### Technical Analysis The chart loads records from `/data/stocks.csv` and places the `ticker`, `name`, and `sector` fields into an HTML template without escaping or sanitization. D3's `.html()` method ultimately assigns the supplied value as HTML rather than plain text. Consequently, a data field containing active HTML, such as an image with an error event handler, will be parsed by the browser. This crosses the security boundary between visualization data and executable document content. The conditional tooltip check only excludes records whose sector is exactly `ETF`; it does not validate or sanitize any displayed field and therefore does not mitigate this issue. ### Attack Path 1. An attacker obtains the ability to add or modify a record in `/data/stocks.csv`, or supplies a data file that is later used by the example. 2. The attacker places an HTML payload in a displayed field, for example: ```html ``` 3. The page loads the malicious record through `d3.csv()`. 4. The record is bound to a bubble without sanitization. 5. A us ...[truncated 965 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/tooltip_handler.js:39
Finding

DOM-based cross-site scripting in the reusable tooltip handler

Content
View full analysis
${data.name || data.id}
${data.value ? 'Value: ' + data.value : ''} `; } ``` The included custom formatter examples also directly interpolate data into HTML: ```javascript formatContent: (d) => ` ${d.ticker}
${d.name}
Sector: ${d.sector} ` ``` ### Technical Analysis `TooltipHandler.show()` treats the result of `formatContent()` as trusted HTML and passes it directly to D3's `.html()` method. The default formatter and supplied examples interpolate data fields without contextual encoding. Because this is a reusable handler, any chart using data from CSV, TSV, JSON, user input, API responses, or other untrusted sources can expose the sink. The `shouldShow` callback controls whether a tooltip appears but provides no sanitization. Custom formatter support further encourages callers to return raw HTML containing unescaped values. The vulnerable design is systemic: the handler's default rendering operation is unsafe unless every caller independently sanitizes every value. ### Attack Path 1. A visualization uses `TooltipHandler` with its default formatter or one of the demonstrated custom HTML formatters. 2. The visualization processes a data object containing attacker-controlled markup in a displayed property such as `name`, ...[truncated 1061 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/check_tooltip.js:34
Finding

DOM-based cross-site scripting in the tooltip content checker

Content
View full analysis
${sampleData.ticker}
${sampleData.name}
Sector: ${sampleData.sector} `; console.log('Tooltip content:', tooltip.innerHTML); // Clean up setTimeout(() => { tooltip.classList.remove('visible'); }, 1000); } ``` ### Technical Analysis `testTooltipContent()` accepts a caller-supplied object and interpolates three properties into a string assigned directly to `innerHTML`. The browser therefore interprets these values as markup rather than text. Although this file is presented as a checker or test utility and the function is not automatically invoked with external data in the audited code, it is globally callable when loaded as a conventional browser script. It becomes exploitable if a caller passes data influenced by an untrusted CSV, JSON file, URL input, API response, or user-controlled value. Removing the `visible` class after one second does not remove the inserted content and does not prevent immediate execution of active markup. ### Attack Path 1. The checker script is loaded into a browser page. 2. Another script or integration calls `testTooltipContent()` with attacker-influenced sample data. 3. A property such as `sampleData.name` contains active HTML. 4. The function interpolates the property into a template and assigns it to `tooltip.innerHTML`. 5. The browser parses the HTML and executes any viable active-content payload in the page's origin. Unlike the hover-driven chart vulnerabilities, this path requires a caller to invoke the helper with un ...[truncated 527 chars]
Remediation
View remediation
{ tooltip.classList.remove('visible'); tooltip.replaceChildren(); }, 1000); return true; } ``` Also validate that `sampleData` is a non-null object with expected primitive field types. If the checker must test rich HTML behavior, use fixed trusted fixtures rather than caller-controlled strings, or sanitize markup with a trusted library before insertion. ]]>
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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 (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description focuses on building deterministic D3.js data visualizations and exporting standalone visual outputs from local data. The supplied code does not generate charts, axes, scales, legends, SVG, HTML documents, or PNGs, and it does not use D3.js at all. Instead, it performs runtime checking and testing of a tooltip element in the DOM, including style inspection, class toggling, and content injection. This is a materially different primary purpose from the declared visualization-generation skill, so the description does not accurately represent the code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description focuses on deterministic generation of data visualizations such as charts, plots, axes, legends, tooltips, and standalone HTML/SVG output from local data. The supplied code instead primarily implements an interactive browser-based data table component. While it uses D3 and could support a visualization workflow, its main function is tabular UI behavior: rendering a table, sorting columns on click, selecting/highlighting rows, coordinating with an external chart via function hooks, formatting numbers, and injecting CSS into the document. This is materially different from the stated primary purpose of generating standalone visualizations, so the description does not accurately represent this code chunk.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The metadata advertises D3.js v6, while the body instructs users to pin and vendor d3@7.9.0. Version mismatches can cause consumers to run the skill with different APIs, semantics, or bundled code than expected, undermining determinism and potentially leading to unsafe workarounds, broken builds, or unintended dependency fetching. In a supply-chain context, inconsistent version guidance increases the chance of loading the wrong artifact or bypassing pinned dependencies.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The activation criteria are broad enough to trigger on generic phrases like 'make a chart' or mentions of legends, axes, and tooltips across many contexts. Over-broad routing can cause the wrong skill to activate, leading the agent to generate HTML/JS artifacts unnecessarily, mishandle user intent, or introduce avoidable complexity and attack surface in workflows that only required simple summaries. While not overtly malicious, this can be exploited indirectly through prompt phrasing to steer the agent into a more powerful toolchain than needed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The script writes tooltip content with innerHTML using fields from sampleData without any escaping or sanitization. If those fields can contain attacker-controlled HTML, this can lead to DOM-based XSS in the verification page or demo environment, especially because the skill processes local data files that may be untrusted.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The determinism section says 'No transitions/animations by default' to avoid timing variance, implying interactive outputs should avoid transition effects unless explicitly justified. Later, the recommended tooltip CSS includes transition: opacity 0.2s, which contradicts that stated intent by reintroducing animation into the default pattern.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.