Back to skill

Security audit

China Travel Planner

Security checks for vulnerabilities and agentic risk

Overview

The skill is mainly a disclosed China travel planner, but its optional page-deployment tooling can modify and publish a Git repository with weak safeguards.

Use the planning and local page-generation features only with trip details you are comfortable putting in files. Do not run the GitHub Pages deploy command unless you are in a clean, dedicated repository, have reviewed the exact files and remote target, and are comfortable making the itinerary public.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
page-generator/scripts/tpf-cli.py:192
Finding
GitHub Pages deployment destructively modifies the active Git working tree<![CDATA[ ## Vulnerability Details **File Location**: `page-generator/scripts/tpf-cli.py:192-265` **Vulnerability Type**: Unsafe repository modification and unintended publication **Risk Level**: High ### Vulnerable Code ```python # Check if gh-pages branch exists result = subprocess.run( ["git", "branch", "--list", "gh-pages"], cwd=project_dir, capture_output=True, text=True ) if "gh-pages" not in result.stdout: # Create orphan branch subprocess.run( ["git", "checkout", "--orphan", "gh-pages"], cwd=project_dir, check=True ) subprocess.run( ["git", "rm", "-rf", "."], cwd=project_dir, check=True ) else: subprocess.run( ["git", "checkout", "gh-pages"], cwd=project_dir, check=True ) # Copy dist contents to root for item in dist_dir.iterdir(): if item.is_file(): shutil.copy(item, project_dir / item.name) # Commit and push subprocess.run(["git", "add", "."], cwd=project_dir, check=True) subprocess.run( ["git", "commit", "-m", "Deploy to GitHub Pages"], cwd=project_dir, check=True, capture_output=True ) subprocess.run( ["git", "push", "origin", "gh-pages"], cwd=project_dir, check=True ) # Get repo URL result = subprocess.run( ["git", "remote", "get-url", "origin"], cwd=project_dir, capture_output=True, text=True, check=True ) repo_url = result.stdout.strip() finally: # Switch back to main branch subprocess.run( ["git", "checkout", "-"], cwd=project_dir, capture_output=True ) ``` ### Technical Analysis The deployment comments describe a temporary worktree, but the implementation does not create one. It checks out the `gh-pages` branch directly in the user's active repository. When the branch does not exist, it creates an orphan branch and executes `git rm -rf .`, removing all tracked content from the active working tree and index. The script then cop ...[truncated 1918 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Deploy from an isolated temporary worktree: ```bash git worktree add --detach <temporary-directory> ``` Alternatively, use a dedicated temporary Git repository containing only the generated artifacts. 2. Never run `git rm -rf .` in the primary working tree. 3. Check repository status before deployment: ```bash git status --porcelain ``` Refuse deployment when local changes exist unless the user explicitly selects a safe override. 4. Copy and stage only allowlisted artifacts, such as: - `index.html` - `trip-data.json` - Explicitly required static assets 5. Avoid `git add .`; pass exact artifact paths to `git add`. 6. Display the resolved remote URL, branch, and files to be published, then require explicit confirmation before pushing. 7. Use `check=True` for restoration operations and report restoration failures prominently. 8. Add a dry-run mode that reports all Git and filesystem changes without modifying the repository. 9. Preserve and clean up temporary worktrees in a guarded `finally` block without altering the caller's checked-out branch. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
page-generator/scripts/tpf-init.sh:22
Finding
Unvalidated project name permits path traversal outside the examples directory<![CDATA[ ## Vulnerability Details **File Location**: `page-generator/scripts/tpf-init.sh:22-38` **Vulnerability Type**: Path traversal and arbitrary file creation **Risk Level**: Medium ### Vulnerable Code ```bash if [ -z "$1" ]; then echo "Usage: bash tpf-init.sh <project-name>" echo "Example: bash tpf-init.sh hangzhou-2026-05" exit 1 fi PROJECT_NAME="$1" PROJECT_DIR="$FRAMEWORK_DIR/examples/$PROJECT_NAME" if [ -d "$PROJECT_DIR" ]; then echo "Error: $PROJECT_DIR already exists" exit 1 fi echo "Creating project: $PROJECT_NAME" mkdir -p "$PROJECT_DIR/data" # ── Create index.html ── cat > "$PROJECT_DIR/index.html" << 'HTMLEOF' ``` The same attacker-influenced directory is subsequently used for additional writes: ```bash cat > "$PROJECT_DIR/data/trip-data.json" << 'JSONEOF' ``` ```bash cat > "$PROJECT_DIR/README.md" << MDEOF ``` ### Technical Analysis The script treats its first argument as a safe project-name component but does not reject path separators or `..` traversal components. Shell quoting prevents command injection, but it does not prevent filesystem path traversal. For example, a value such as `../../new-project` produces a path equivalent to: ```text <framework>/examples/../../new-project ``` After path normalization, the target is outside the intended `examples` directory. If the selected final directory does not already exist, the script creates it and writes predictable files into it. The existing-directory check reduces direct overwrite of an existing directory, but it does not enforce containment and does not prevent arbitrary creation of a new directory elsewhere within the invoking user's writable filesystem. ### Attack Path 1. An attacker, untrusted automation, or malformed Agent-generated value supplies a project name containing traversal components, such as `../../new-project`. 2. The script concatenates the value with `FRAMEWORK_DIR/examples`. 3. The operating system resolves the `..` components outside the exampl ...[truncated 763 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict project names to a single safe path component: ```bash if [[ ! "$PROJECT_NAME" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]; then echo "Error: invalid project name" >&2 exit 1 fi ``` 2. Explicitly reject: - `/` and `\` - `..` - Empty names - Control characters - Leading hyphens if later commands could interpret the name as an option 3. Resolve the destination to a canonical path and verify that it is a child of the canonical examples directory before creating it. 4. Create the project directory atomically and fail if it already exists: ```bash mkdir "$PROJECT_DIR" mkdir "$PROJECT_DIR/data" ``` 5. Avoid `mkdir -p` for the final untrusted directory because it can silently create a traversal-selected hierarchy. 6. Add tests covering names such as: - `../../outside` - `name/subdirectory` - `../examples2` - Empty and control-character-containing values ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_subway_data.py:9
Finding
Metro network data is retrieved over unauthenticated plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fetch_subway_data.py:9-20, 50-55` **Vulnerability Type**: Unauthenticated plaintext network transport **Risk Level**: Medium ### Vulnerable Code ```python INDEX_URL = "http://map.amap.com/subway/index.html?&1100" DETAIL_URL = "http://map.amap.com/service/subway?srhdata={city_id}_drw_{cityname}.json" HEADERS = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0 Safari/537.36" } def fetch_index() -> str: resp = requests.get(INDEX_URL, headers=HEADERS, timeout=20) resp.raise_for_status() resp.encoding = "utf-8" return resp.text ``` ```python def fetch_city_detail(city_id: str, cityname: str) -> Dict: url = DETAIL_URL.format(city_id=city_id, cityname=cityname) resp = requests.get(url, headers=HEADERS, timeout=20) resp.raise_for_status() data = resp.json() return data ``` ### Technical Analysis Both the city index and detailed metro data are retrieved using HTTP rather than HTTPS. HTTP does not authenticate the server and does not protect response integrity. An on-path attacker can alter the index HTML used to resolve city identifiers or replace the detailed JSON response. The code accepts the response after status checking and JSON parsing, without cryptographic verification or comprehensive schema and plausibility validation. The attacker-controlled response is normalized and used as planning input. This is a data-integrity vulnerability rather than a direct local code-execution vulnerability because the response is parsed as data and is not passed to `eval`, a shell, or another executable interpreter. ### Attack Path 1. A user or Agent invokes `fetch_subway_data.py`, directly or through trip generation with metro support. 2. The script sends HTTP requests to the AMap endpoints. 3. An attacker controlling or observing the local network, proxy, gateway, or DNS path intercepts the requests. 4. Th ...[truncated 904 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace both endpoints with verified HTTPS equivalents supported by the service. 2. Do not silently downgrade to HTTP if HTTPS fails. 3. Retain normal certificate and hostname verification; do not introduce `verify=False`. 4. Validate response headers and reject unexpected content types. 5. Apply strict response-size limits before parsing large responses. 6. Validate the returned structure against an explicit schema, including: - Expected top-level fields - String and array types - Reasonable maximum line and station counts - Expected city identifiers - Length limits for all names and identifiers 7. Consider recording the source URL and retrieval timestamp in downstream output so users can assess freshness and provenance. 8. If no authenticated endpoint exists, clearly warn users that the data is unauthenticated and require confirmation before using it for safety-sensitive route decisions. ]]>

T03 · Remote Payload Retrieval and Execution

Warning
Location
page-generator/templates/trip-page-tailwind.html:7
Finding
Generated pages execute mutable third-party JavaScript from an unpinned CDN<![CDATA[ ## Vulnerability Details **File Location**: `page-generator/templates/trip-page-tailwind.html:7` **Additional Location**: `page-generator/scripts/tpf-init.sh:43` **Vulnerability Type**: Remote mutable code execution and supply-chain exposure **Risk Level**: Medium ### Vulnerable Code The page template contains: ```html <script src="https://cdn.tailwindcss.com"></script> ``` The initialization script emits the same runtime dependency into newly generated projects: ```html <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>旅行计划</title> <script src="https://cdn.tailwindcss.com"></script> ``` ### Technical Analysis Every generated page loads and executes JavaScript from `cdn.tailwindcss.com` when opened. The URL is not tied to an immutable version, and the page does not specify Subresource Integrity. No restrictive Content Security Policy is present to constrain the loaded script. Consequently, the effective JavaScript payload can change after the Skill package has been reviewed. A compromise of the CDN, its account, DNS resolution, or the relevant upstream publication process could cause arbitrary JavaScript to execute in visitors' browsers. The generated build inlines the local theme and renderer scripts but leaves this external Tailwind runtime reference in place. Therefore, the output is not fully self-contained from a security or availability perspective. ### Attack Path 1. A user generates or initializes a travel page. 2. The page retains the external `https://cdn.tailwindcss.com` script reference. 3. The page is hosted or opened in a browser. 4. The browser requests the current JavaScript payload from the CDN. 5. If the external source or delivery path is compromised, the browser executes the substituted script. 6. The malicious script runs in the page's browser context and can inspect or alter page content and initiate network requests permitted by the browser. ### ...[truncated 702 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Compile Tailwind CSS during the build process and ship the resulting static CSS file instead of executing the CDN runtime. 2. Pin all build dependencies to reviewed, immutable versions using an appropriate lockfile. 3. If an external asset is unavoidable, use an immutable versioned URL and Subresource Integrity where supported. 4. Add a restrictive Content Security Policy. A static page should generally avoid allowing arbitrary remote scripts. 5. Update the build and initialization scripts so that generated pages contain no mutable runtime JavaScript dependency. 6. Document all remaining remote resources and their privacy, availability, and supply-chain implications. 7. Add an automated build check that fails when generated HTML contains unapproved external `<script src>` references. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (58)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill says it uses flyai/Fliggy plus metro data for broad planning, but some documented behavior only retrieves subway data from AMap and exports it via CLI. That is a narrower and different operational profile than the user-facing promise, which increases the chance of trust abuse and improper permissioning.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill says it uses flyai/Fliggy plus metro data for broad planning, but some documented behavior only retrieves subway data from AMap and exports it via CLI. That is a narrower and different operational profile than the user-facing promise, which increases the chance of trust abuse and improper permissioning.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill says it uses flyai/Fliggy plus metro data for broad planning, but some documented behavior only retrieves subway data from AMap and exports it via CLI. That is a narrower and different operational profile than the user-facing promise, which increases the chance of trust abuse and improper permissioning.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill says it uses flyai/Fliggy plus metro data for broad planning, but some documented behavior only retrieves subway data from AMap and exports it via CLI. That is a narrower and different operational profile than the user-facing promise, which increases the chance of trust abuse and improper permissioning.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill says it uses flyai/Fliggy plus metro data for broad planning, but some documented behavior only retrieves subway data from AMap and exports it via CLI. That is a narrower and different operational profile than the user-facing promise, which increases the chance of trust abuse and improper permissioning.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The skill says it uses flyai/Fliggy plus metro data for broad planning, but some documented behavior only retrieves subway data from AMap and exports it via CLI. That is a narrower and different operational profile than the user-facing promise, which increases the chance of trust abuse and improper permissioning.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill says it uses flyai/Fliggy plus metro data for broad planning, but some documented behavior only retrieves subway data from AMap and exports it via CLI. That is a narrower and different operational profile than the user-facing promise, which increases the chance of trust abuse and improper permissioning.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
- optional swap-ins
- niche / backup choices

## Output rules

- Always return a **curated plan**, not raw command output.
- If flyai returns image URLs, place the image line before the booking link.
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill is described as a China travel planning capability, but this file implements local site building and GitHub Pages deployment, including repository mutation and publication. That mismatch increases risk because an agent invoking a travel-planning skill would not reasonably expect source-control manipulation or remote publishing side effects, enabling surprising file changes and data exposure.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The code performs git branch switching, file deletion, commit creation, and remote push operations that are unjustified for a travel-planning skill. In agent contexts, unnecessary repository and publishing privileges materially expand attack surface and can lead to unauthorized code/content publication or destructive workspace changes if the skill is invoked unexpectedly.

Missing User Warnings

High
Confidence
98% confidence
Finding
The deploy flow performs branch checkout, repository-wide file removal, staging, commit, and push with no explicit confirmation or dry-run. In an agent setting this is highly dangerous because it combines destructive local changes with external publication, making accidental invocation capable of causing data loss and unauthorized release of content.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill documentation describes use of shell commands, network access, file reads, and file writes, but it does not declare any explicit tool scope or allowed-tools boundary. That creates an overprivileged and ambiguous execution model where an agent could invoke capabilities beyond what a travel-planning skill strictly needs, increasing the chance of unintended command execution or data handling.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The trigger description is extremely broad and could cause the skill to activate for many generic travel requests, even when its advanced behaviors are unnecessary. Overbroad routing is dangerous in context because it increases the chances that users are exposed to hidden file/build/publish workflows when they expected simple travel advice.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The documentation expands from travel assistance into a full page-generation pipeline that creates artifacts, validates them, builds a site, and prepares deployment. This is a scope increase into software/project automation, which creates additional risk around file writes, shell execution, and publishing that users would not reasonably infer from a travel-planner skill.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill documents GitHub Pages deployment even though public publishing is not justified by the core travel-planning purpose. A deployment step can expose itinerary details, personal travel dates, hotel choices, or other sensitive trip data to the public if triggered without strong safeguards.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The deployment instructions omit any warning that generated travel pages may publish personal or sensitive itinerary information. In a travel context, dates, cities, hotels, and movement plans can create privacy and safety risks if exposed publicly without informed consent.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The document sets lang="zh-CN" and the visible interface text throughout the page is in Chinese, which imposes a specific language/locale on users. The file does not provide any opt-in, language selector, or documentation indicating that this is intentionally limited to a China-specific audience.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
Line L042 instructs authors to keep the summary under 90 Chinese characters when possible, which imposes a specific language/locale expectation in natural language. The file does not indicate that Chinese output is optional, user-selected, or required for a clearly documented region-specific purpose.

Intent-Code Divergence

Medium
Confidence
92% confidence
Finding
The module usage text and cmd_validate docstring say the tool validates trip-data.json against a schema, and a schema path is defined. In practice, the code never reads or applies the schema file; it only checks JSON syntax plus a few required keys and list types, which contradicts the documented intent.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The build command unconditionally deletes and recreates the dist directory, which can destroy user data or overwrite expected artifacts without a warning or confirmation step. In an agent-executed context, non-interactive destructive file operations are dangerous because users may not realize a travel-related action can modify local project contents.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
error("Not a git repository. Run: git init")

    # Check remote
    result = subprocess.run(
        ["git", "remote", "-v"],
        cwd=project_dir,
        capture_output=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
        # Check if gh-pages branch exists
        result = subprocess.run(
            ["git", "branch", "--list", "gh-pages"],
            cwd=project_dir,
            capture_output=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if "gh-pages" not in result.stdout:
            # Create orphan branch
            subprocess.run(
                ["git", "checkout", "--orphan", "gh-pages"],
                cwd=project_dir,
                check=True
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
cwd=project_dir,
                check=True
            )
            subprocess.run(
                ["git", "rm", "-rf", "."],
                cwd=project_dir,
                check=True
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
check=True
            )
        else:
            subprocess.run(
                ["git", "checkout", "gh-pages"],
                cwd=project_dir,
                check=True
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.