Back to skill

Security audit

Travel Partner

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a disclosed travel-content generator, but it has real local file-write and remote-download safety issues that users should review before installing.

Install only if you are comfortable with optional OpenAI API use, prompt data leaving your environment during image generation, local files being created, and the current path-handling/download weaknesses. Run it in a dedicated working directory, avoid sensitive personal details in prompts, use a scoped API key, and prefer pinned dependencies.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/itinerary_generator.py:384
Finding
Path Traversal Through Itinerary Output Filename<![CDATA[ ## Vulnerability Details **File Location**: `scripts/itinerary_generator.py`, lines 384-385 **Vulnerability Type**: Unsanitized user-controlled output path **Risk Level**: Medium ### Vulnerable Code ```python destination = sys.argv[1] duration = int(sys.argv[2]) trip_type = sys.argv[3] if len(sys.argv) > 3 else "romantic" generator = TravelItineraryGenerator(destination, duration, trip_type) itinerary = generator.generate_full_itinerary() # Save to file output_file = f"{destination.lower()}_itinerary_{duration}days.json" save_itinerary_to_file(itinerary, output_file) ``` The resulting path is passed to the following file-writing operation: ```python def save_itinerary_to_file(itinerary, output_file="travel_itinerary.json"): with open(output_file, 'w', encoding='utf-8') as f: json.dump(itinerary, f, indent=2, ensure_ascii=False) ``` ### Technical Analysis The `destination` command-line argument is incorporated directly into an output filename. The code does not reject absolute paths, path separators, `..` components, or other filesystem metacharacters. Because Python's `open(..., "w")` follows the constructed path and truncates an existing file, a destination containing traversal components can cause the itinerary to be written outside the intended working directory. The fixed `_itinerary_<duration>days.json` suffix limits which filenames can be targeted, but it does not confine writes to an approved directory. ### Attack Path 1. An attacker gains control over the destination argument, directly or through an application that invokes the script. 2. The attacker supplies a traversal value such as `../shared/report`. 3. The script constructs a path similar to: ```text ../shared/report_itinerary_5days.json ``` 4. `save_itinerary_to_file` opens the path in write mode. 5. A file outside the intended output directory is created or an existing matching file is overwritten. ### Impact Assessment Exploitation grants filesystem ...[truncated 476 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store all generated files beneath a fixed output directory. 2. Convert the destination to a strict filename slug containing only approved characters, such as ASCII letters, digits, hyphens, and underscores. 3. Reject absolute paths, `..`, forward slashes, backslashes, NUL characters, and empty filenames. 4. Resolve the final path and verify that it remains beneath the approved output directory. 5. Avoid silently truncating existing files; use exclusive creation or require explicit overwrite approval. 6. Consider rejecting symbolic-link output targets. Example hardening: ```python import re from pathlib import Path OUTPUT_DIR = Path("generated_itineraries").resolve() OUTPUT_DIR.mkdir(parents=True, exist_ok=True) def safe_slug(value): slug = re.sub(r"[^A-Za-z0-9_-]+", "_", value).strip("_") if not slug: raise ValueError("Destination does not produce a valid filename") return slug safe_destination = safe_slug(destination) output_file = ( OUTPUT_DIR / f"{safe_destination}_itinerary_{duration}days.json" ).resolve() if OUTPUT_DIR not in output_file.parents: raise ValueError("Output path escapes the approved directory") ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/travel_experience_generator.py:509
Finding
Path Traversal Through Travel Content Output Filenames<![CDATA[ ## Vulnerability Details **File Location**: `scripts/travel_experience_generator.py`, lines 509-519 **Vulnerability Type**: Unsanitized user-controlled output paths **Risk Level**: Medium ### Vulnerable Code ```python destination = sys.argv[1] persona = sys.argv[2] if len(sys.argv) > 2 else "romantic_companion" generator = TravelExperienceGenerator(destination, persona) # Generate sample content sample_activities = [ {'time': '09:00', 'name': 'Explore Louvre', 'location': 'Musee du Louvre', 'photo_spot': 'Louvre Pyramid'}, {'time': '14:00', 'name': 'Seine River Cruise', 'location': 'Seine River', 'photo_spot': 'Seine River Cruise'}, {'time': '17:30', 'name': 'Visit Eiffel Tower', 'location': 'Eiffel Tower', 'photo_spot': 'Eiffel Tower Sunset'}, {'time': '20:00', 'name': 'French Dinner', 'location': 'Restaurant', 'photo_spot': 'Romantic Restaurant'} ] # Generate journal journal = generator.generate_daily_journal(1, sample_activities) save_content_to_file(journal, f"{destination.lower()}_journal_day1.md") # Generate guide guide = generator.generate_travel_guide({}) save_content_to_file(guide, f"{destination.lower()}_guide.md") # Generate stories stories = generator.generate_interesting_stories() save_content_to_file(stories, f"{destination.lower()}_stories.md") ``` The generated paths reach this write operation: ```python def save_content_to_file(content, output_file): """Save content to file""" with open(output_file, 'w', encoding='utf-8') as f: f.write(content) print(f"✅ Content saved to: {output_file}") ``` ### Technical Analysis The attacker-controlled `destination` value is directly embedded into three output paths. No filename normalization or containment check is performed. Traversal components can consequently escape the current directory. Each destination value causes three separate writes with the suffixes `_journal_day1.md`, `_guide.md`, and `_stories.md`. Existing matching files are truncated b ...[truncated 1003 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Write generated content only beneath a dedicated directory. 2. Sanitize destination names using an allowlist rather than removing selected dangerous characters. 3. Reject absolute paths, separators, `.` and `..` path components, and control characters. 4. Resolve each output path and enforce containment beneath the dedicated directory. 5. Check for symbolic links and avoid unintended overwrites. 6. Centralize secure path construction so all three outputs receive identical validation. Example: ```python import re from pathlib import Path OUTPUT_DIR = Path("generated_experiences").resolve() OUTPUT_DIR.mkdir(parents=True, exist_ok=True) def build_output_path(destination, suffix): slug = re.sub(r"[^A-Za-z0-9_-]+", "_", destination).strip("_") if not slug: raise ValueError("Invalid destination") candidate = (OUTPUT_DIR / f"{slug}{suffix}").resolve() if OUTPUT_DIR not in candidate.parents: raise ValueError("Output path escapes the approved directory") return candidate journal_path = build_output_path(destination, "_journal_day1.md") guide_path = build_output_path(destination, "_guide.md") stories_path = build_output_path(destination, "_stories.md") ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_travel_image.py:108
Finding
Unrestricted Remote Resource Download Enables SSRF and Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_travel_image.py`, lines 108-123 **Vulnerability Type**: Unvalidated remote URL download **Risk Level**: Medium ### Vulnerable Code ```python def download_image(self, url, output_path): """ Download image from URL Args: url: Image URL output_path: Local file path """ import requests try: response = requests.get(url, timeout=30) response.raise_for_status() with open(output_path, 'wb') as f: f.write(response.content) return True except Exception as e: raise Exception(f"Error downloading image: {str(e)}") ``` ### Technical Analysis The function performs an HTTP request to any supplied URL without validating: - The URL scheme. - The destination hostname. - The resolved IP address. - Redirect destinations. - Whether the target is loopback, private, link-local, or reserved. - The response `Content-Type`. - The response size. - Whether the response is actually a valid image. `requests.get` follows redirects by default. It also buffers the complete response body in memory through `response.content`, after which the entire body is written to disk. In the normal command-line workflow, the URL comes from the OpenAI image-generation response, which reduces direct attacker control. However, `download_image` is a public method and creates an unrestricted network-fetch primitive for integrations or future callers. A compromised, mocked, or attacker-influenced upstream response could also supply a malicious URL. ### Attack Path 1. An attacker obtains influence over the `url` passed to `download_image`, such as through direct programmatic use, an integration layer, or an attacker-controlled upstream response. 2. The attacker provides a URL targeting an internal service, loopback endpoint, private address, or cloud metadata endpoint. 3. The function sends the request from the environment running the Sk ...[truncated 1015 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit only HTTPS URLs. 2. Restrict downloads to documented OpenAI image-storage hostnames. 3. Resolve hostnames before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IP ranges. 4. Disable redirects or validate the scheme, hostname, and resolved address at every redirect hop. 5. Use streamed downloads instead of `response.content`. 6. Enforce a strict maximum response size using both `Content-Length` and an incremental byte counter. 7. Require an approved image MIME type. 8. Validate image signatures and decode the image with a trusted image library before accepting it. 9. Write to a temporary file and atomically rename it only after validation succeeds. 10. Apply restrictive file permissions and clean up partial downloads after failures. Example download pattern: ```python import ipaddress import socket from urllib.parse import urlparse import requests MAX_IMAGE_BYTES = 20 * 1024 * 1024 ALLOWED_HOSTS = {"oaidalleapiprodscus.blob.core.windows.net"} def validate_url(url): parsed = urlparse(url) if parsed.scheme != "https" or parsed.hostname not in ALLOWED_HOSTS: raise ValueError("Unapproved image URL") for result in socket.getaddrinfo(parsed.hostname, 443): address = ipaddress.ip_address(result[4][0]) if ( address.is_private or address.is_loopback or address.is_link_local or address.is_multicast or address.is_reserved or address.is_unspecified ): raise ValueError("Unsafe image host address") def download_image(url, output_path): validate_url(url) total = 0 with requests.get( url, stream=True, timeout=(5, 30), allow_redirects=False ) as response: response.raise_for_status() content_type = response.headers.get("Content-Type", "") if not content_type.startswith("image/"): ...[truncated 360 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
Findings (21)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior materially exceeds or differs from the declared user-facing purpose, especially by invoking external image-generation services, downloading remote content, and writing artifacts locally. Description-behavior mismatch is dangerous because reviewers and users may authorize the skill for innocuous companion-content generation while it performs networked and file-writing operations with higher privacy and security implications.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The documented behavior materially exceeds or differs from the declared user-facing purpose, especially by invoking external image-generation services, downloading remote content, and writing artifacts locally. Description-behavior mismatch is dangerous because reviewers and users may authorize the skill for innocuous companion-content generation while it performs networked and file-writing operations with higher privacy and security implications.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior materially exceeds or differs from the declared user-facing purpose, especially by invoking external image-generation services, downloading remote content, and writing artifacts locally. Description-behavior mismatch is dangerous because reviewers and users may authorize the skill for innocuous companion-content generation while it performs networked and file-writing operations with higher privacy and security implications.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The documented behavior materially exceeds or differs from the declared user-facing purpose, especially by invoking external image-generation services, downloading remote content, and writing artifacts locally. Description-behavior mismatch is dangerous because reviewers and users may authorize the skill for innocuous companion-content generation while it performs networked and file-writing operations with higher privacy and security implications.

Credential Access

High
Category
Privilege Escalation
Content
"pip install openai"
            )

        # Get API key from parameter or environment
        api_key = api_key or os.environ.get('OPENAI_API_KEY')
        if not api_key:
            raise ValueError(
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
'prompt': prompt
        })

    return prompts


def generate_landmark_photo_prompt(destination, landmark):
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
'prompt': prompt
        })

    return prompts


def generate_landmark_photo_prompt(destination, landmark):
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises and documents capabilities that require environment access, file reads/writes, and network access, but it does not declare any explicit tool scope or permission boundaries. This increases the chance that a host agent may invoke the skill with broader-than-necessary privileges, enabling unintended API-key access, network transmission, and local file creation without clear governance.

Vague Triggers

Medium
Confidence
86% confidence
Finding
An overly broad invocation description can cause the orchestrator to route generic travel-content requests to this skill even when the user did not ask for romantic-companion simulation, image generation, or external lookups. That increases the risk of unnecessary tool use, privacy leakage, or content generation outside user expectations due to ambiguous matching.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill instructs users to set an API key, call an external image API, download returned images, and write them locally, but it does not provide a prominent user-facing warning about data leaving the environment or files being created. In agent settings, undisclosed network transmission and local writes can surprise users and create privacy, cost, and persistence risks, especially when prompts may contain sensitive itinerary or relationship-context details.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest describes a virtual travel companion that creates travel narratives, photo suggestions, and itinerary planning. This script goes further by invoking the OpenAI image API to generate images and then downloading and saving binary image files locally, which is materially broader than merely suggesting photo ideas.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The embedded itinerary tips and descriptions are written entirely in English, including guidance such as "Learning a few local phrases will be very helpful," without offering a user language or locale option. Under the policy, forcing a specific language without user opt-in is a natural-language policy concern.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This Python file contains natural-language output that always labels emergency contact information as "Chinese Embassy," even though the skill is presented as a general-purpose travel generator. That imposes a specific nationality/locale assumption without user opt-in or documented justification, which is a natural-language policy violation under the locale policy rule.

Missing User Warnings

Low
Confidence
82% confidence
Finding
This markdown file instructs users to set an OpenAI API key and use `generate_travel_image.py`, which sends travel/location prompts to an external service and saves generated outputs locally. The README explains setup and cost, but does not warn users that prompts and related metadata may be transmitted to a third-party API or that local files/manifests will be created.

Natural-Language Policy Violations

Low
Confidence
75% confidence
Finding
The README presents the journal, blog, and social content outputs in English and suggests fixed English phrasing such as 'My love...' and English travel blog structures, but does not mention that users can choose another language or locale. For a skill aimed at international destinations, this can amount to an implicit language-default policy without user opt-in.

Unpinned Dependencies

Low
Category
Supply Chain
Content
openai>=1.0.0
requests>=2.31.0
Confidence
97% confidence
Finding
The dependency specification for `openai` is unpinned and allows any future version at or above 1.0.0 to be installed. This creates supply-chain and reproducibility risk because newer releases may introduce breaking changes or security issues without review, even though this file alone does not prove an exploitable flaw today.

Unpinned Dependencies

Low
Category
Supply Chain
Content
openai>=1.0.0
requests>=2.31.0
Confidence
99% confidence
Finding
The dependency specification for `requests` is unpinned, so installation may resolve to different versions over time, including versions with known vulnerabilities. Because `requests` has a history of security advisories and is commonly used for outbound network access, leaving it unpinned increases the chance of silently pulling a vulnerable release.

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
98% confidence
Finding
The manifest includes `requests` without an exact version, and the package has multiple known advisories affecting some releases. Since the actual installed version cannot be verified from this manifest, there is a credible risk that deployments may resolve to a vulnerable version, which is especially relevant for a travel assistant skill likely to perform external HTTP requests.

Context-Inappropriate Capability

Low
Confidence
80% confidence
Finding
The manifest frames the skill as a travel companion for narratives, immersive experiences, and social media content, but does not indicate credential handling or secret access. This file retrieves OPENAI_API_KEY from the environment in order to call an external image service, which is a capability outside the user-facing purpose as described.

Description-Behavior Mismatch

Low
Confidence
77% confidence
Finding
The manifest frames the skill as providing virtual travel companion content, personalized narratives, photo suggestions, and itinerary planning. This script additionally persists outputs to disk by default, which is an operational behavior not reflected in the description and goes beyond pure content generation.

Description-Behavior Mismatch

Low
Confidence
89% confidence
Finding
The manifest frames the skill as providing AI travel companion experiences, narratives, photo suggestions, and itinerary planning. In addition to generating that content, this script persists multiple outputs to disk via save_content_to_file and creates destination-named markdown files, which is behavior beyond the user-facing description.

Static analysis

No suspicious patterns detected.