Back to skill

Security audit

travel-destination-brochure

Security checks for vulnerabilities and agentic risk

Overview

The skill largely matches its travel-brochure purpose, but its setup guidance encourages unsafe remote installer execution and risky API-key handling.

Install only after removing the pipe-to-shell installer paths, using pinned dependencies where possible, and handling VLMRUN_API_KEY through a secure secret mechanism. Do not let an agent read .env files or print full API keys, and run image downloading in a constrained output directory because remote media is not strongly validated.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
Findings (4)

T03 · Remote Payload Retrieval and Execution

Error
Location
README.md:48
Finding
Unverified Remote Installer Is Downloaded and Executed Directly## Vulnerability Details **File Location**: `README.md:48-60`; `SKILL.md:49-64` **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: High ### Vulnerable Code `README.md:48-60`: ```powershell # Windows (PowerShell) pip install uv # Or using installer powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" ``` ```bash # macOS/Linux pip install uv # Or using installer curl -LsSf https://astral.sh/uv/install.sh | sh ``` `SKILL.md:49-64`: ```powershell # Using pip pip install uv # Or using PowerShell installer powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" ``` ```bash # Using pip pip install uv # Or using curl installer curl -LsSf https://astral.sh/uv/install.sh | sh ``` ### Technical Analysis Both installation guides instruct users to retrieve mutable code from an external URL and pass it directly to a command interpreter. The downloaded content is not pinned to a version, inspected, or verified using a checksum or cryptographic signature. The PowerShell variant additionally starts a process with `ExecutionPolicy ByPass`. Although this does not necessarily grant administrative privileges, it removes a local script-execution safeguard for that process. HTTPS authenticates the server connection but does not ensure that the retrieved installer remains identical to the version reviewed with this project. The effective code can change following an upstream account compromise, hosting compromise, dependency compromise, or unauthorized modification of the installer. This behavior is not required for the Skill's core functionality because the same documentation already provides `pip install uv` as an alternative. It therefore exceeds the minimum-risk installation method necessary for the declared travel-brochure workflow. ### Attack Path 1. An attacker compromises the upstream installer host, publi ...[truncated 940 chars]
Remediation
## Remediation Suggestions 1. Remove both pipe-to-interpreter installation alternatives from `README.md` and `SKILL.md`. 2. Prefer installation through a trusted package manager using an exact reviewed version: ```bash python -m pip install "uv==REVIEWED_VERSION" ``` 3. If a standalone installer is necessary, split download and execution into separate steps. 4. Pin the installer to a versioned URL rather than a mutable generic endpoint. 5. Verify the downloaded artifact against a checksum or signature published through an independent trusted channel. 6. Avoid `ExecutionPolicy ByPass`; use an appropriately signed script and the least-permissive execution policy. 7. Tell users to inspect the downloaded script before execution and not to run installation commands from an elevated shell unless explicitly necessary.

T08 · Insecure Dependencies

Warning
Location
scripts/simple_travel_brochure.py:2
Finding
Third-Party Dependencies Are Installed Without Version or Integrity Pinning## Vulnerability Details **File Location**: `README.md:63-69`; `SKILL.md:94-101`; `scripts/geocode_city.py:2-5`; `scripts/fetch_commons.py:2-5`; `scripts/fetch_openstreetcam.py:2-5`; `scripts/run_travel_pipeline.py:2-5`; `scripts/simple_travel_brochure.py:2-5` **Vulnerability Type**: Unpinned third-party dependency resolution **Risk Level**: Medium ### Vulnerable Code `README.md:63-69` and `SKILL.md:94-101` install unconstrained package versions: ```bash # Install vlmrun CLI (required for video and travel plan generation) uv pip install "vlmrun[cli]" # Install requests (required for API calls) uv pip install requests ``` Each Python script also contains an unconstrained PEP 723 dependency declaration, for example `scripts/simple_travel_brochure.py:2-5`: ```python # /// script # requires-python = ">=3.10" # dependencies = ["requests"] # /// ``` ### Technical Analysis The project does not pin exact dependency versions or provide a lockfile containing verified artifact hashes. Consequently, installation and `uv run` can resolve package releases that were not part of this audit. This is a supply-chain exposure rather than evidence that the named packages are currently malicious. The risk arises because a future compromised, malicious, or unexpectedly incompatible release can be selected automatically. Python packages can execute code during installation, import, or CLI startup, so a compromised dependency may gain access to the invoking process's files, environment variables, and network permissions. The broad `vlmrun[cli]` extra can also introduce a larger transitive dependency graph than a narrowly pinned runtime package. ### Attack Path 1. An attacker compromises the publisher account, release pipeline, or a transitive dependency for one of the unconstrained packages. 2. A malicious package release is published under a version accepted by the unconstrained declarations. 3. A user runs the documented ...[truncated 655 chars]
Remediation
## Remediation Suggestions 1. Pin direct dependencies to exact reviewed versions. 2. Generate and commit a lockfile that includes all transitive dependencies. 3. Require package hashes where supported and verify packages against trusted indexes. 4. Keep PEP 723 declarations synchronized with the lockfile or replace ad hoc script resolution with a locked project environment. 5. Review the dependency tree introduced by `vlmrun[cli]` and install only the features required by this Skill. 6. Use automated vulnerability and provenance scanning for every dependency update. 7. Perform updates through reviewed pull requests rather than resolving the latest available releases at runtime.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_commons.py:55
Finding
Remote Responses Can Trigger Unbounded and Unvalidated File Downloads## Vulnerability Details **File Location**: `scripts/fetch_commons.py:55-65,109-129`; `scripts/fetch_openstreetcam.py:31-41,88-108`; `scripts/simple_travel_brochure.py:125-142` **Vulnerability Type**: Unrestricted remote URL retrieval and unsafe file ingestion **Risk Level**: Medium ### Vulnerable Code `scripts/fetch_commons.py:55-65`: ```python def download_file(url: str, path: Path) -> bool: try: r = requests.get(url, headers=HEADERS, timeout=30, stream=True) r.raise_for_status() path.parent.mkdir(parents=True, exist_ok=True) with open(path, "wb") as f: for chunk in r.iter_content(chunk_size=8192): f.write(chunk) return True except Exception: return False ``` The URL is taken directly from the API response at `scripts/fetch_commons.py:109-129`: ```python ii = (p.get("imageinfo") or [None])[0] if not ii: continue url = ii.get("url") or ii.get("thumburl") or "" if not url: continue extmeta = ii.get("extmetadata") or {} desc = (extmeta.get("ImageDescription") or {}).get("value") or "" obj = (extmeta.get("ObjectName") or {}).get("value") or "" caption = (obj or desc or p.get("title", "")).replace("File:", "").strip() if len(caption) > 500: caption = caption[:497] + "..." title = p.get("title", "File:unknown.jpg") entry = {"index": i, "title": title, "url": url, "caption": caption or title} if not args.no_download: fname = out_dir / safe_filename(title, i) if download_file(url, fname): entry["path"] = str(fname.resolve()) else: entry["path"] = None ``` `scripts/fetch_openstreetcam.py:31-41` contains equivalent unrestricted streaming: ```python def download_file(url: str, path: Path) -> bool: try: r = requests.get(url, headers=HEADERS, timeout=30, stream=True) r.raise_for_status() path.parent.mkdir(parents=True, exi ...[truncated 3159 chars]
Remediation
## Remediation Suggestions 1. Permit only `https` URLs. 2. Allowlist documented Wikimedia and OpenStreetCam media hostnames. 3. Disable automatic redirects or validate the scheme and hostname after every redirect. 4. Reject responses whose declared `Content-Length` exceeds a conservative image-size limit. 5. Count streamed bytes and abort if the limit is exceeded, even when `Content-Length` is absent or false. 6. Require an allowlisted image MIME type and reject generic binary or executable content. 7. Decode the file with a maintained image library, enforce pixel and decompression limits, and re-encode it into a safe normalized format. 8. Download to a newly created temporary file and atomically move it only after successful validation. 9. Remove partial files following errors. 10. Apply separate upload limits before passing validated images to VLM Run.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:114
Finding
Documentation Encourages Reading, Printing, and Persisting Full API Keys## Vulnerability Details **File Location**: `SKILL.md:114-143`; related diagnostic guidance in `README.md:314-321` **Vulnerability Type**: Plaintext secret exposure through agent and terminal workflows **Risk Level**: Medium ### Vulnerable Code `SKILL.md:114-143`: ```powershell # Set for current session Check .env file for api key $env:VLMRUN_API_KEY="your-api-key-here" # Set permanently (User-level) [System.Environment]::SetEnvironmentVariable('VLMRUN_API_KEY', 'your-api-key-here', 'User') ``` ```bash # Set for current session export VLMRUN_API_KEY="your-api-key-here" # Set permanently (add to ~/.bashrc or ~/.zshrc) echo 'export VLMRUN_API_KEY="your-api-key-here"' >> ~/.bashrc source ~/.bashrc ``` ```text Read **.env** file to find api keys ``` ```bash # Windows PowerShell echo $env:VLMRUN_API_KEY # macOS/Linux echo $VLMRUN_API_KEY ``` ### Technical Analysis Verifying that a credential exists does not require reading or printing its complete value. In an agent-oriented Skill, the instruction to read `.env` can unnecessarily place secret material into the agent's context, tool history, or audit logs. Printing the complete environment variable can expose it through terminal scrollback, shell recording, screenshots, CI logs, or shared transcripts. The documentation also recommends persisting the key as plaintext in shell initialization files or the user-level environment. This increases the secret's lifetime and exposes it to processes and users capable of reading those locations. The Python scripts themselves do not directly read `.env` or print the API key, and `.env_template.txt` contains placeholders only. The issue is therefore confined to unsafe operational instructions rather than confirmed automated exfiltration. ### Attack Path 1. A user stores a real VLM Run API key in `.env` or an environment variable. 2. An agent follows the instruction to read `.env`, or a user follo ...[truncated 937 chars]
Remediation
## Remediation Suggestions 1. Remove instructions telling an agent to read `.env` files or retrieve API-key values. 2. Replace full-value diagnostics with presence-only checks: ```bash test -n "$VLMRUN_API_KEY" && echo "VLMRUN_API_KEY is set" || echo "VLMRUN_API_KEY is not set" ``` 3. For PowerShell, test presence without displaying the value: ```powershell if ($env:VLMRUN_API_KEY) { "VLMRUN_API_KEY is set" } else { "VLMRUN_API_KEY is not set" } ``` 4. Use an operating-system credential store, CI secret facility, or dedicated secret manager instead of shell profile files. 5. Ensure real `.env` files are excluded from version control and restricted using filesystem permissions. 6. Never pass API keys through command-line arguments because they can appear in process listings and shell history. 7. Redact secrets from logs and agent tool outputs. 8. Document key rotation and immediate revocation procedures for accidentally exposed credentials.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (40)

Chaining Abuse

High
Category
Tool Misuse
Content
# macOS/Linux
   pip install uv
   # Or using installer
   curl -LsSf https://astral.sh/uv/install.sh | sh
   ```

3. **Install Dependencies**
Confidence
97% confidence
Finding
The shell pipeline `curl -LsSf https://astral.sh/uv/install.sh | sh` is a classic command-chaining pattern that turns remote content into immediate code execution. In a skill README, this is especially risky because users may copy-paste it verbatim, allowing compromise of the local environment if the fetched script is malicious or tampered with.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The code only implements a Wikimedia Commons image fetcher/downloader. While this is one supporting piece of the declared workflow, it does not perform the core advertised functions: creating travel brochures/scenarios, generating travel videos, planning travel, or fetching OpenStreetCam imagery. The resource usage is narrower than declared, and the primary purpose of this code chunk is media retrieval/manifest creation rather than end-to-end travel content generation.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The code chunk implements only a narrow image-ingestion utility for OpenStreetCam. While this could be a supporting subcomponent of a larger travel brochure workflow, the declared purpose describes an end-user skill that builds destination scenarios/brochures from a city name and generates travel videos and plans using multiple data sources. None of those higher-level functions appear in this code. The resource access is also only partial relative to the description, since Wikimedia Commons is absent. This is therefore a material description-to-behavior mismatch rather than a minor implementation detail.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
There is a clear description-behavior mismatch. The declared purpose centers on travel content generation from city input using image sources and a VLM pipeline. The actual code performs only a geocoding utility function: it sends the city query to Nominatim, retrieves location search results, and outputs latitude/longitude and display name. This is at most a supporting subcomponent for a larger travel workflow, but on its own it does not implement the described primary functionality. It also accesses a different external resource (Nominatim/OpenStreetMap) than the declared OpenStreetCam/Wikimedia/VLM stack.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code substantially matches the imagery collection portion of the description: it geocodes a city, fetches OpenStreetCam and Wikimedia Commons images via helper scripts, and prepares a combined manifest. However, the declared purpose says the skill then uses vlmrun to generate a travel video and a travel plan, and implies creation of travel brochure/scenario outputs. This code does not call vlmrun at all; it only writes image paths and prints a 'next_step' instruction for a separate manual vlmrun invocation. So the implemented behavior is an asset-preparation pipeline, not the full brochure/video/travel-plan generation capability claimed in the description.

Chaining Abuse

High
Category
Tool Misuse
Content
pip install uv

# Or using curl installer
curl -LsSf https://astral.sh/uv/install.sh | sh
```

**Verify installation:**
Confidence
93% confidence
Finding
Chaining a network fetch directly into shell execution is a classic arbitrary-code-execution anti-pattern. In an agent setting, this is especially dangerous because it encourages blind execution of remote content without verification or user review.

Credential Access

High
Category
Privilege Escalation
Content
**Windows (PowerShell):**
```powershell
# Set for current session
Check .env file for api key

$env:VLMRUN_API_KEY="your-api-key-here"
Confidence
99% confidence
Finding
The skill directs credential access by telling the user/agent to inspect a .env file for an API key. Credential retrieval from local files is highly sensitive and unnecessary for a brochure-generation skill, creating a real risk of unauthorized secret exposure.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The instructions explicitly tell the operator/agent to read local .env files to obtain API keys, which is unrelated to the minimum needs of generating a travel brochure. This encourages secret discovery from the local environment and can lead to unauthorized credential access and downstream exfiltration or misuse.

Ssd 3

High
Confidence
99% confidence
Finding
The plain-language instruction to check a local .env file for an API key is a direct credential-access action. In an agentic context, this crosses from ordinary setup guidance into instructing the system to retrieve secrets from local storage, which is highly sensitive.

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
*
```powershell
# Set for current session
Check .env file for api key

$env:VLMRUN_API_KEY="your-api-key-here"

# Set permanently (User-level)
[System.Environment]::SetEnvironmentVariable('VLMRUN_API_KEY', 'your-api-key-here', 'User')
```

**macOS/Linux:**
```bash
# Set for current session
export VLMRUN_API_KEY="your-api-key-here"

# Set permanently (add to ~/.bashrc or ~/.zshrc)
echo 'export VLMRUN_API_KEY="your-api-key-here"' >> ~/.bashrc
source ~/.bashrc
```

**Verify environment variable:**

Read **.env** file to find api keys

```bash
# Windows PowerShell
echo $env:VLMRUN_API_KEY

# macOS/Linux
echo $VLMRUN_API_KEY
```

### Step 6: Verify Installation

Test that everything works:

```bash
# Test geocoding (should work without API key)
uv run scripts/geocode_city.py "Paris, France"

# Test vlmrun (if API key is set)
vlmrun --help
```

**Installation Complete!** You're ready to generate travel brochures.

## Quick Start (Recommended)
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Ssd 3

High
Confidence
99% confidence
Finding
The verification step again instructs reading a .env file to find API keys, reinforcing secret harvesting behavior. Repeated credential-access guidance materially increases the risk that an agent will access secrets it should not inspect.

External Transmission

Medium
Category
Data Exfiltration
Content
### OpenStreetCam API

- **Base URL**: `https://api.openstreetmap.org/`
- **Documentation**: [API Reference](https://api.openstreetcam.org/api/doc.html)
- **Key Endpoints**:
  - `POST /1.0/list/nearby-photos/` - Get photos near coordinates
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Lp3

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding
The skill advertises and instructs use of shell, network access, and file writes but does not declare any explicit tool scope or permissions boundaries. In an agent setting, missing scope increases the chance the skill can run with broader-than-necessary capabilities, making other risky instructions in the file more dangerous.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
If Python is not installed or is an older version:
- **Windows**: Download from [python.org](https://www.python.org/downloads/)
- **macOS**: `brew install python@3.11` (or use python.org installer)
- **Linux**: `sudo apt install python3.11` (Ubuntu/Debian) or use your distribution's package manager

### Step 2: Install uv (Package Manager)
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
uv --version
```

### Step 3: Create Virtual Environment

Navigate to the skill directory and create a virtual environment:
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill normalizes reading a .env file for secrets and echoing or handling the API key without any safeguards. Even verification steps that print credentials risk exposing them in terminal history, logs, screenshots, or agent transcripts.

Session Persistence

Medium
Category
Rogue Agent
Content
# Set for current session
export VLMRUN_API_KEY="your-api-key-here"

# Set permanently (add to ~/.bashrc or ~/.zshrc)
echo 'export VLMRUN_API_KEY="your-api-key-here"' >> ~/.bashrc
source ~/.bashrc
```
Confidence
90% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
Passing an API key directly on the command line exposes the secret to shell history, process listings, telemetry, and logs. In multi-user or monitored environments, this can leak the credential beyond the intended workflow.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

OSC_BASE = "https://api.openstreetcam.org"
OSC_PHOTO_BASE = "https://api.openstreetcam.org/"
NEARBY_PHOTOS = f"{OSC_BASE}/1.0/list/nearby-photos/"
HEADERS = {"User-Agent": "TravelDestinationBrochure/1.0 (skill)"}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

OSC_BASE = "https://api.openstreetcam.org"
OSC_PHOTO_BASE = "https://api.openstreetcam.org/"
NEARBY_PHOTOS = f"{OSC_BASE}/1.0/list/nearby-photos/"
HEADERS = {"User-Agent": "TravelDestinationBrochure/1.0 (skill)"}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

OSC_BASE = "https://api.openstreetcam.org"
OSC_PHOTO_BASE = "https://api.openstreetcam.org/"
NEARBY_PHOTOS = f"{OSC_BASE}/1.0/list/nearby-photos/"
HEADERS = {"User-Agent": "TravelDestinationBrochure/1.0 (skill)"}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

OSC_BASE = "https://api.openstreetcam.org"
OSC_PHOTO_BASE = "https://api.openstreetcam.org/"
NEARBY_PHOTOS = f"{OSC_BASE}/1.0/list/nearby-photos/"
HEADERS = {"User-Agent": "TravelDestinationBrochure/1.0 (skill)"}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

OSC_BASE = "https://api.openstreetcam.org"
OSC_PHOTO_BASE = "https://api.openstreetcam.org/"
NEARBY_PHOTOS = f"{OSC_BASE}/1.0/list/nearby-photos/"
HEADERS = {"User-Agent": "TravelDestinationBrochure/1.0 (skill)"}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

OSC_BASE = "https://api.openstreetcam.org"
OSC_PHOTO_BASE = "https://api.openstreetcam.org/"
NEARBY_PHOTOS = f"{OSC_BASE}/1.0/list/nearby-photos/"
HEADERS = {"User-Agent": "TravelDestinationBrochure/1.0 (skill)"}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

OSC_BASE = "https://api.openstreetcam.org"
OSC_PHOTO_BASE = "https://api.openstreetcam.org/"
NEARBY_PHOTOS = f"{OSC_BASE}/1.0/list/nearby-photos/"
HEADERS = {"User-Agent": "TravelDestinationBrochure/1.0 (skill)"}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.