Back to skill

Security audit

AI Travel Agent

Security checks for vulnerabilities and agentic risk

Overview

The travel-planning skill is mostly coherent, but its instructions contain a concrete API-key exposure risk and loose handling of optional calendar access.

Review before installing. The travel searches and optional calendar features fit the stated purpose, but do not run the documented commands with `--key $SERPAPI_KEY`; use the environment variable or credential files instead, and only allow calendar access after explicitly choosing the date range and calendar action.

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

Warning
Location
SKILL.md:138
Finding

SerpAPI Credential Exposed Through Command-Line Arguments

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:138-148, SKILL.md:166-175, and SKILL.md:189-199
Vulnerability Type: API credential exposure through process arguments
Risk Level: Medium

Vulnerable Code

bash
# Ground transport (trains, buses, ferries)
python3 scripts/search_transport.py \
  --from "City A, Country" --to "City B, Country" \
  --key $SERPAPI_KEY

# Domestic flights
python3 scripts/search_flights.py \
  --from [IATA_A] --to [IATA_B] --date YYYY-MM-DD \
  --adults N --class economy --currency GBP \
  --key $SERPAPI_KEY

The same insecure argument is also included in the single-destination flight and hotel examples:

bash
python3 scripts/search_flights.py \
  --from [IATA] --to [IATA] \
  --date YYYY-MM-DD [--return-date YYYY-MM-DD] \
  --adults N [--children N] \
  --class economy|premium_economy|business|first \
  --currency GBP \
  --key $SERPAPI_KEY
bash
python3 scripts/search_hotels.py \
  --location "[CITY] [preference]" \
  --check-in YYYY-MM-DD --check-out YYYY-MM-DD \
  --adults N [--children N] \
  [--stars N] [--amenities "pool,beach"] \
  --sort highest_rating \
  --currency GBP \
  --key $SERPAPI_KEY

Technical Analysis

Shell expansion replaces $SERPAPI_KEY with the plaintext credential before starting the Python process. The resulting API key may therefore appear in process argument listings, command auditing systems, diagnostic output, execution logs, or shell traces.

These instructions directly contradict the Skill's stated credential-handling policy and the scripts' implementation, which load the key from the environment or designated credential files. The Python argument parsers do not define a --key option, meaning the documented commands will also terminate with an unrecognized-argument error after the shell has already expanded and exposed the secret.

Attack Path

  1. A user or Agent follows o ...[truncated 850 chars]
Remediation
View remediation

Remediation Suggestions

  • Remove every --key $SERPAPI_KEY argument from SKILL.md.

  • Invoke the scripts without putting the key on the command line. Their existing load_api_key() functions already retrieve it from SERPAPI_KEY, ~/.serpapi_credentials, or ~/.travel_agent_config.

  • Update the examples to use commands such as:

    bash
    python3 scripts/search_transport.py \
      --from "City A, Country" --to "City B, Country"
    
  • Add automated documentation checks that reject examples containing --key, api_key=, or direct credential interpolation.

  • Rotate the SerpAPI key if any of the documented commands have previously been executed in an environment with command auditing, shell tracing, or untrusted local users.

  • Restrict credential-file permissions to the owning user, for example with mode 0600.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/search_flights.py:58
Finding

External HTTP Requests Lack Explicit Timeouts

Content
View full analysis

Vulnerability Details

File Location: scripts/search_flights.py:58-59, scripts/search_hotels.py:61-62, and scripts/search_transport.py:47-48
Vulnerability Type: Unbounded external network operations
Risk Level: Low

Vulnerable Code

scripts/search_flights.py:

python
resp = requests.get("https://serpapi.com/search", params=params)
resp.raise_for_status()

scripts/search_hotels.py:

python
resp = requests.get("https://serpapi.com/search", params=params)
resp.raise_for_status()

scripts/search_transport.py:

python
resp = requests.get("https://serpapi.com/search", params=params)
resp.raise_for_status()

Technical Analysis

Python Requests does not apply a default timeout. Without an explicit timeout, a connection or response that stalls can keep the process blocked indefinitely. None of the three scripts catches timeout or general Requests exceptions to produce a controlled failure.

The endpoint is fixed to the declared HTTPS SerpAPI domain, so this issue does not provide arbitrary server-side request forgery or code execution. It is an availability and resilience weakness affecting all executable search workflows.

Attack Path

  1. The user or Agent starts a flight, hotel, or transport search.
  2. SerpAPI, an intermediary network component, or the local network accepts the connection but stops making progress.
  3. Because no timeout is configured, the HTTP call remains blocked.
  4. The search process and any Agent workflow waiting for it fail to complete until externally terminated.

Impact Assessment

The issue does not grant additional privileges or expose local files. Its impact is limited to availability: individual search processes can hang, Agent tasks can become unresponsive, and automated workflows may exhaust worker capacity if multiple stalled requests accumulate.

Remediation
View remediation

Remediation Suggestions

  • Configure separate connection and read timeouts on every external request:

    python
    resp = requests.get(
        "https://serpapi.com/search",
        params=params,
        timeout=(5, 30),
    )
    
  • Catch requests.Timeout, requests.ConnectionError, and broader requests.RequestException errors and return a controlled nonzero exit status.

  • Avoid printing request parameters when handling exceptions because they contain the SerpAPI key.

  • Consider bounded retries with exponential backoff and jitter for transient failures. Do not retry authentication errors or other permanent client errors.

  • Apply the same network-handling helper consistently across all three scripts to prevent configuration drift.

Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a broad travel-planning assistant with multiple capabilities: flights, hotels, calendar checking, destination advice, seasonal guidance, internal transport, and fully costed single- or multi-stop itinerary generation. The supplied code chunk implements only one narrow function: flight search through SerpAPI/Google Flights for a user-specified origin, destination, and dates. It does not perform hotel lookup, calendar integration, destination discovery, itinerary assembly, regional multi-stop planning, or full cost estimation. Additionally, the description says the skill requires a SerpAPI key stored in ~/.serpapi_credentials, while the code also accepts the key from the SERPAPI_KEY environment variable and ~/.travel_agent_config. This is a significant underimplementation relative to the declared purpose, so the description does not accurately represent what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description presents a broad travel-planning assistant with multiple major capabilities: flights, hotels, calendar checking, itinerary construction, destination/date recommendation, and multi-stop planning. The supplied code only implements hotel search against SerpAPI's Google Hotels endpoint, with simple filtering and formatting of results. While hotel search is consistent with part of the description, the actual behavior is materially narrower than the declared primary purpose. Additionally, the description says the key is stored in ~/.serpapi_credentials, whereas the code also supports SERPAPI_KEY from the environment and ~/.travel_agent_config. That credential-loading detail alone would be minor, but the larger issue is that this chunk does not perform most of the assistant's declared functions.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description presents a broad travel-planning assistant with flight/hotel search, calendar checking, destination/date guidance, and full itinerary building. The supplied code only performs one specific subtask: searching transit/ground transport between two locations via SerpAPI. While ground transport could be a supporting travel-planning feature, this code chunk does not implement the core declared capabilities and has a much narrower primary purpose. Resource access is broadly consistent with SerpAPI credential use, but the behavior materially underdelivers relative to the declared functionality.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The description says to use the skill whenever a user wants to plan a trip, including cases with only dates, only a destination, or neither fully specified. This is very broad natural-language activation guidance and does not provide clear boundaries or negative examples, increasing the chance of unintended invocation for general travel conversation.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The invocation guidance says to use this when a user 'doesn't know where to go' or 'wants to go somewhere but doesn't know when,' which is a very broad natural-language condition likely to overlap with many ordinary travel conversations. It does not define exclusions, concrete trigger phrases, or negative examples to clarify when this skill should not activate.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file instructs the agent to load the user's Google Calendar and inspect free windows without any requirement to first obtain explicit user consent or warn that personal schedule data will be accessed. In a travel assistant context, this can lead to unnecessary access to sensitive personal information and over-collection of data beyond the user's immediate request.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The reference explicitly documents how to turn flight search results into direct booking links, which conflicts with the skill's declared 'NOT for booking' boundary. Even if the skill only intends to present options, giving implementation guidance for booking-link retrieval makes it easier for downstream prompts or future code changes to cross that boundary and initiate transactional flows the user may not expect.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The transport section emphasizes that direction results include direct operator booking URLs, which again contradicts the manifest's non-booking limitation. In a travel-planning skill, this increases the chance the agent will surface or prioritize transactional links, blurring the line between planning assistance and booking enablement.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README states that the SerpAPI credential files are the only local files the skill reads, but nearby documentation also describes optional calendar availability checks and calendar event creation. This creates a misleading trust boundary: users may believe the skill has narrower data access than it actually can via calendar integrations, which can affect informed consent and security review.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The example command hard-codes --currency GBP, and the notes later state GBP is the default if the user's preference is not set. This imposes a specific locale/currency convention without explicit user choice, which can conflict with language/locale policy expectations.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The note instructs the skill to default to GBP when no user preference is set. That is a locale-specific default applied without opt-in, which may violate policy requiring user choice for locale-sensitive behavior.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The document labels the section as "Europe" at L013, but the first itinerary under it is "Japan" at L015-L016. This is an active contradiction in the documentation rather than a mere omission, and could mislead the agent when selecting a route template.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The request parameters force hl to en and gl to uk, which imposes a specific language and geographic locale regardless of user preference. This matches the policy category for language/locale constraints because the script does not offer any opt-in or configurable choice for locale.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.