Back to skill

Security audit

chartjs-reporter

Security checks for vulnerabilities and agentic risk

Overview

This chart-report skill has a coherent purpose, but its generated HTML can execute untrusted report data as JavaScript and automatically loads browser code from a CDN.

Review this skill before installing. It is not showing hidden persistence or credential theft, but only use it with trusted data unless the generator is fixed to escape HTML, validate chart options, avoid automatic preview of untrusted reports, and either bundle Chart.js or add integrity protection and clear CDN 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_report.py:40
Finding
Stored HTML and JavaScript Injection Through Unescaped Report Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_report.py`, lines 40–44; additional affected sinks at lines 143–145, 165, 177, 190, 224, and 318–319 **Vulnerability Type**: Stored HTML injection / cross-site scripting **Risk Level**: High ### Vulnerable Code ```python cards.append(f""" <div class="kpi"> <div class="kpi-label">{kpi.get('label', '')}</div> <div class="kpi-value" style="color:{color_var}">{kpi.get('value', '')}</div> <div class="kpi-sub">{kpi.get('sub', '')}</div> </div>""") ``` The same unsafe interpolation pattern is used in other report sections: ```python cards_html.append(f""" <div class="chart-card"> <h3>{title}</h3> <div class="chart-wrap"><canvas id="chart{i}"></canvas></div> </div>""") ``` ```python header_cells = "".join([f"<th>{col}</th>" for col in columns]) ``` ```python value = row.get(col, "") cell_html = f"<td>{value}</td>" ``` ```python <title>{title}</title> ``` ```python <h1>📊 {title}</h1> <p class="subtitle">{subtitle}</p> ``` ```python <footer>{footer}</footer> ``` ### Technical Analysis Report content is inserted directly into HTML through Python formatted strings without context-appropriate escaping. Affected inputs include: - Report title, subtitle, and footer - KPI labels, values, and subtitles - Chart and table titles - Table column names and cell values These values may originate from command-line arguments, JSON input, database query results, CSV files, or direct calls to `build_report`. Consequently, imported data cannot safely be treated as trusted markup. An attacker can supply an HTML element with an event handler, such as an image whose error handler executes JavaScript. The payload is preserved in the generated report and runs when a user opens that report in a browser. This is a stored injection issue because the payload is written into the output HTML before execution. ### Attack Path 1. An attacker places mal ...[truncated 1263 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape every value inserted into HTML text using `html.escape`: ```python from html import escape def _html_text(value) -> str: return escape(str(value), quote=True) ``` 2. Apply the helper to all report title, subtitle, footer, KPI, chart title, table title, column, and cell values: ```python label = _html_text(kpi.get("label", "")) value = _html_text(kpi.get("value", "")) sub = _html_text(kpi.get("sub", "")) ``` 3. Do not use one generic transformation for every output context. Use: - HTML escaping for element text. - Attribute escaping for attribute values. - JSON serialization for JavaScript values. - Strict validation for enumerated configuration values. 4. Treat CSV, SQL, JSON, and query-derived values as untrusted, even when the report is generated locally. 5. Consider adding a restrictive Content Security Policy. If scripts remain inline, migrate them to a separately generated trusted script or use hashes/nonces rather than enabling unrestricted inline script execution. 6. Add regression tests covering payloads in every supported text field, including tags, quotes, event handlers, closing elements, and Unicode edge cases. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_report.py:50
Finding
JavaScript-Context Injection Through Unvalidated Chart Type and Fill Values<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_report.py`, lines 50–111 **Vulnerability Type**: JavaScript code injection **Risk Level**: High ### Vulnerable Code ```python def _chart_js_config(chart: dict, idx: int) -> str: ctype = chart.get("type", "bar") labels = json.dumps(chart.get("labels", []), ensure_ascii=False) title = chart.get("title", f"图表{idx+1}") raw_datasets = chart.get("datasets", []) ``` The `fill` property is converted to text and inserted as a raw JavaScript expression: ```python elif ctype == "line": fill = str(ds.get("fill", True)).lower() ds_str = f"""{{ label: {label}, data: {data}, borderColor: "{color}", backgroundColor: "{color}22", tension: 0.4, fill: {fill}, pointRadius: 4, pointBackgroundColor: "{color}" }}""" ``` The chart type is inserted into a single-quoted JavaScript string without JavaScript-safe serialization: ```python return f"""new Chart(document.getElementById('chart{idx}'), {{ type: '{ctype}', data: {{ labels: {labels}, datasets: [{datasets_js}] }}, ``` ### Technical Analysis The documentation lists a finite set of supported chart types, but the implementation does not enforce that allowlist. The `ctype` value is embedded directly between single quotes in executable JavaScript. A crafted value containing a quote and JavaScript syntax can terminate the string and inject statements into the generated script. The `fill` value is also unsafe. Although it is intended to be a Boolean, the implementation accepts arbitrary input, converts it to a string, and places it into JavaScript without quoting or JSON serialization. An attacker can therefore supply a syntactically valid JavaScript expression with side effects. Other chart values such as labels and data are serialized with `json.dumps`, which is safer for ordinary JavaScript syntax construction. However, H ...[truncated 1281 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce the documented chart-type allowlist before generating output: ```python ALLOWED_CHART_TYPES = { "bar", "line", "doughnut", "pie", "horizontalBar", } ctype = chart.get("type", "bar") if ctype not in ALLOWED_CHART_TYPES: raise ValueError(f"Unsupported chart type: {ctype!r}") ``` 2. Require `fill` to be a Boolean rather than converting arbitrary objects to raw JavaScript: ```python fill = ds.get("fill", True) if not isinstance(fill, bool): raise ValueError("Dataset 'fill' must be a boolean") ``` 3. Serialize all JavaScript values with `json.dumps`: ```python type_js = json.dumps(ctype, ensure_ascii=False) fill_js = json.dumps(fill) ``` 4. Prefer constructing the complete Chart.js configuration as a validated Python dictionary and serializing it once, rather than assembling executable JavaScript fragments with formatted strings. 5. When embedding serialized JSON inside an HTML `<script>` element, neutralize HTML parser termination sequences. For example, replace `<` with `\u003c` after serialization or place configuration JSON in a non-executable data block that is safely parsed at runtime. 6. Add schema validation for chart objects, including allowed types, Boolean fields, dataset structure, labels, and numeric data arrays. 7. Add tests proving that quotes, semicolons, JavaScript expressions, and `</script>` strings cannot escape their intended data contexts. ]]>

T08 · Insecure Dependencies

Note
Location
scripts/generate_report.py:229
Finding
Unverified Runtime Chart.js Dependency Loaded from a Third-Party CDN<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_report.py`, line 229 **Vulnerability Type**: Third-party supply-chain exposure without Subresource Integrity **Risk Level**: Low ### Vulnerable Code ```html <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> ``` ### Technical Analysis Every generated report loads and executes Chart.js from jsDelivr at viewing time. Although the package version is pinned to `4.4.0`, the script element does not include a Subresource Integrity hash. The browser therefore has no cryptographic mechanism to verify that the fetched resource matches a specific audited artifact. This dependency also means the generated report is not fully self-contained, despite the project documentation describing it that way. Opening a report produces an external network request and makes report functionality dependent on CDN availability. ### Attack Path 1. The generator writes the third-party script reference into each report. 2. A user opens the report while connected to a network. 3. The browser requests the Chart.js asset from jsDelivr. 4. If the CDN response, package artifact, delivery path, or trusted transport environment is compromised, the browser receives modified JavaScript. 5. Because no integrity attribute is present, the browser executes the response without checking it against an expected hash. ### Impact Assessment A compromised dependency response could execute arbitrary JavaScript in every opened report that loads it. Such code could read or modify the report DOM, falsify visualizations, and make additional network requests subject to browser restrictions. This finding does not demonstrate that the current CDN asset is malicious. It identifies a supply-chain hardening gap and a runtime availability/privacy dependency. The immediate privilege scope remains the generated report's browser context. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer bundling a reviewed Chart.js distribution directly into the generated HTML so reports are genuinely self-contained and do not retrieve executable code at viewing time. 2. If CDN delivery must remain, calculate and pin the correct Subresource Integrity hash for the exact asset: ```html <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js" integrity="sha384-REPLACE_WITH_VERIFIED_HASH" crossorigin="anonymous"></script> ``` The hash must be generated from and verified against the exact production artifact; it must not be copied from an untrusted source. 3. Retain an exact dependency version and establish a controlled process for reviewing and updating the bundled asset or integrity hash. 4. Add an appropriate Content Security Policy restricting scripts and outbound connections to explicitly required sources. 5. Update the documentation to disclose the runtime network dependency if it is retained, rather than describing the output as fully self-contained. 6. Test report behavior without network access and define whether failure to load the dependency should produce a visible, non-deceptive error. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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 (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The skill claims to produce a self-contained report, but it relies on an external Chart.js CDN and describes additional behavior not cleanly declared in the purpose statement. This mismatch can mislead users and orchestrators about network dependency, data exposure in the browser, and actual runtime behavior, which weakens trust and security review assumptions.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill instructs the agent to write an HTML file to disk, but the manifest does not declare any tool scope such as file-write permissions or allowed tools. This creates a capability-governance gap: an agent may create files without explicit user-visible authorization boundaries, increasing the chance of unintended file creation or abuse by adjacent workflow logic.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger conditions are broad enough to activate on many generic requests for reports or visualization, which can cause the skill to run outside a narrowly intended context. Overbroad activation increases the chance of unnecessary file generation, external resource loading, or incorrect delegation in sensitive workflows.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The markdown trigger section uses ambiguous criteria like needing 'visualization' or 'report' without strong scope boundaries, making unintended activation likely. In an agentic environment, ambiguous routing can expose data to unnecessary transformations or file outputs that the user did not specifically request.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill says it will write an HTML report and open it in a browser, but it does not warn the user that this creates a file and loads Chart.js from an external CDN. That omission matters because opening the report may trigger network access and could expose sensitive labels or metadata in a browser context the user did not expect.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
This markdown file is written entirely in Chinese, including the title, examples, and usage guidance, with no indication that the user can choose another language or that the skill is intended only for a Chinese-speaking or region-specific context. Under the policy rule for natural-language violations, forcing a specific language without opt-in is a reportable issue.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The generated document sets <html lang="zh-CN"> unconditionally, which imposes a specific language/locale choice in the output. The file also uses Chinese text throughout its interface and usage strings without indicating that this is optional or region-specific.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The report claims to be self-contained but loads Chart.js from a third-party CDN at runtime. This breaks the trust boundary: opening the generated HTML performs a network fetch and makes the report dependent on external infrastructure, which can enable supply-chain compromise, privacy leakage, or report failure in offline/restricted environments.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
This markdown file presents the skill reference entirely in Chinese, starting with the title and continuing throughout the document, with no indication that users may choose another language or that the locale restriction is required. The policy requires flagging language or locale constraints when they are imposed without user opt-in or documented justification.

Missing User Warnings

Low
Confidence
85% confidence
Finding
The script performs a file write via Path(args.output).write_text(...), which modifies the local filesystem. Although the CLI exposes an --output argument, there is no confirmation prompt or explicit warning about overwriting an existing file before the write occurs.

Static analysis

No suspicious patterns detected.