Back to skill

Security audit

dashboard-generator

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its dashboard-generation purpose, but generated dashboards can expose raw input data and may execute injected content from untrusted data.

Install only if you are comfortable reviewing or patching the generated HTML path. Use trusted, sanitized data; do not publish dashboards generated from confidential API responses, customer data, secrets, or third-party CSV/JSON until the output encoding issue is fixed and the Chart.js dependency is bundled or protected with integrity checks.

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/dashboard_gen.py:236
Finding

Stored Cross-Site Scripting in Generated HTML Dashboards

Content
View full analysis

{chart["title"]}

' ``` ```python charts_js += f''' new Chart(document.getElementById('{chart["id"]}'), {json.dumps(config)});''' ``` The generated values are subsequently inserted into the final document: ```python
{chart_canvases}
``` ```python ``` ### Technical Analysis The generator processes attacker-controllable JSON and CSV content, including column names and categorical values. These values are used to construct chart titles, labels, and dataset labels. Chart titles are inserted directly into an HTML heading without HTML escaping: ```python

{chart["title"]}

``` Consequently, a malicious column name containing HTML can introduce arbitrary elements or event handlers into the generated dashboard. Chart configuration data is passed through `json.dumps()` and embedded in an inline `` can terminate the surrounding script at the HTML parser level, even when that sequence appears inside a valid JavaScript string. The remainder of the input can then inject a new executable script element. The table and dashboard title use `html.escape()`, but the chart title and inline chart configuration paths do not receive equivalent contextual output encoding. ### Attack Path 1. An attacker creates a JSON or CSV file with a malicio ...[truncated 1453 chars]
Remediation
View remediation
' f'

{escape(chart["title"])}

' f'' f'' ) ``` 2. Do not embed untrusted JSON directly into an executable inline script. Store chart configurations in non-executable JSON elements and read them through `textContent`. 3. Before embedding serialized JSON in HTML, escape characters significant to the HTML parser: ```python def safe_json_for_html(value): return ( json.dumps(value) .replace("&", "\\u0026") .replace("<", "\\u003c") .replace(">", "\\u003e") .replace("\u2028", "\\u2028") .replace("\u2029", "\\u2029") ) ``` 4. Generate internal canvas identifiers independently rather than deriving identifiers from untrusted data. 5. Add a restrictive Content Security Policy. Prefer external or nonce-protected scripts and avoid permitting unrestricted inline JavaScript. 6. Add regression tests covering malicious column names and values, including: ```text "> ``` The tests should verify that these strings are displayed only as text and cannot terminate or introduce HTML or script elements. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/dashboard_gen.py:336
Finding

External Chart.js Dependency Loaded Without Subresource Integrity

Content
View full analysis
``` ### Technical Analysis Every generated dashboard loads executable JavaScript from jsDelivr when opened. Although the Chart.js version is pinned to `4.4.1`, the script element does not specify a Subresource Integrity hash. Without integrity verification, the browser trusts whatever content is returned for the CDN URL. A compromise of the CDN, package distribution path, account, or relevant delivery infrastructure could therefore replace the expected Chart.js code with attacker-controlled JavaScript. This also means the generated dashboard is not fully standalone: opening it triggers an outbound request and chart functionality depends on continued availability of an external service. ### Attack Path 1. A user generates a dashboard using the project. 2. The user or a recipient opens the dashboard while connected to the network. 3. The browser requests Chart.js from the configured jsDelivr URL. 4. An attacker who has compromised an applicable supply-chain or content-delivery component causes a modified response to be served. 5. Because no integrity hash is present, the browser accepts and executes the substituted JavaScript. 6. The malicious dependency accesses or alters dashboard content and may transmit displayed data externally. ### Impact Assessment A successfully substituted CDN script executes with the same browser context as the dashboard. It can: - Read all data displayed in charts, tables, and KPI cards. - Modify the dashboard or falsify reported results. - Send dashboard data to a remote service. - Exercise privileges available to the dashboard's origin when the file is hosted. - Affect all generated dashboards opened while the compromised dependency is being se ...[truncated 150 chars]
Remediation
View remediation
``` The integrity value must be computed from or copied from a trusted source for the exact referenced asset; it must not be guessed. 3. Add a restrictive Content Security Policy that permits scripts only from explicitly approved locations and avoids broad directives such as `unsafe-inline`. 4. Document that generated dashboards require a network request unless Chart.js is embedded locally. 5. Establish a dependency-update process that verifies the exact Chart.js artifact, records its cryptographic hash, and tests updated versions before changing the pinned dependency. ]]>
Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • 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)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill documents file-reading and file-writing behavior through its commands, but it does not declare any tool scope or permissions boundaries. In an agent environment, missing explicit scope increases the chance of overbroad file access assumptions and makes it harder for operators to constrain what the skill may read or write.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documentation encourages turning arbitrary JSON, CSV, or piped API responses into standalone shareable HTML, but it does not warn that the generated dashboard may contain raw sensitive data, derived metrics, or embedded tables. Users may unintentionally publish confidential operational, customer, or internal API data in an easily distributable file.

Content

No source excerpt is available for this finding.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/dashboard_gen.py (reported line 465)May include surrounding context.

python
except json.JSONDecodeError:
            data = parse_csv(input_text)
        html = generate_html(data, args.title, args.palette, args.description)
        Path = __import__("pathlib").Path
        Path(args.output).write_text(html)
        print(f"✅ Dashboard saved to {args.output} ({len(data)} rows)")
        return

Static analysis

No suspicious patterns detected.