Back to skill

Security audit

Ski Assistant

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a real ski assistant, but it needs Review because it stores personal trip/coaching data and can create scheduled reminders despite saying it does not create scheduled tasks.

Install only if you are comfortable with a ski assistant storing local profile, trip, watchlist, and coaching-history files, using external services for searches and live data, and creating scheduled reminders after consent. Review reminder settings, avoid broad update-db use unless you trust the upstream database source, and treat flyai results as coming from an external executable/service.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (3)

T06 · System Persistence

Error
Location
modules/pre-departure.md:35
Finding

Persistent Cron-Based Reminder and Monitoring Tasks

Content
View full analysis

Vulnerability Details

File Location: modules/pre-departure.md:35-44, modules/pre-departure.md:215-233, and modules/presale.md:57,129-130
Vulnerability Type: Cross-session scheduled-task persistence
Risk Level: High

Vulnerable Instruction Snippet

markdown
3. Ask the user whether scheduled reminder tasks should be created. Explicit user consent is required.
4. After the user agrees, create two scheduled tasks:
   - 20:00 on the day before departure: send an equipment checklist and weather summary.
   - 07:00 on the departure date: send a final checklist, live weather, and traffic reminder.

### Scenario C: Scheduled reminder execution through cron

1. Read the corresponding trip information.
2. Query live weather.
3. Generate the checklist.
4. Send the result to the user's IM session, if connected, or output it in the conversation.

The module also instructs the Agent to delete associated cron tasks when a reminder is cancelled. The presale module separately permits periodic checks through the Agent platform's cron capability.

Technical Analysis

These instructions authorize the creation of scheduled tasks that survive the conversation in which the Skill was invoked. The tasks subsequently read persistent trip or watchlist data, make external network requests, and potentially send messages through a connected IM channel.

Explicit user consent is required before scheduling, which reduces the risk of covert installation. Nevertheless, the resulting mechanism is system persistence because execution continues across sessions and outside the original interactive request.

This behavior also conflicts with the statement in SKILL.md:136 that the Skill does not create background services, daemons, or scheduled tasks. The contradiction may prevent users and reviewers from accurately understanding the Skill's persistence behavior.

No concrete cron command or malicious backdoor payload is em ...[truncated 1661 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove cron creation from the Skill and use user-triggered checks whenever persistent execution is not essential.
  2. If reminders are required, use a platform-native one-shot reminder API rather than arbitrary cron commands.
  3. Display the exact execution time, timezone, action, data accessed, network destinations, and expiry before requesting consent.
  4. Restrict scheduled jobs to a fixed, audited handler rather than allowing the Agent to construct arbitrary shell commands.
  5. Assign every job a visible identifier and store only the minimum data required to execute it.
  6. Automatically expire reminder jobs after their scheduled execution and monitoring jobs after a short, predefined period.
  7. Verify cancellation by querying the scheduler after deletion, and report any deletion failure to the user.
  8. Prevent scheduled jobs from accessing unrelated files, credentials, tools, or communication channels.
  9. Require separate, explicit permission before sending messages to an external IM channel.
  10. Update SKILL.md and the README so that the declared behavior accurately discloses all scheduling and cross-session execution capabilities.

T08 · Insecure Dependencies

Warning
Location
tools/resort_discovery.py:543
Finding

Unpinned Remote Database Replacement with Insufficient Integrity Validation

Content
View full analysis

Vulnerability Details

File Location: tools/resort_discovery.py:44,543-571
Vulnerability Type: Mutable upstream supply-chain dependency
Risk Level: Medium

Vulnerable Code Snippet

python
_GITHUB_RAW_URL = "https://raw.githubusercontent.com/wjyhahaha/ski-assistant/main/data/resorts_db.json"

def update_db() -> str:
    import shutil

    backup_path = _DB_PATH + ".bak"
    if os.path.exists(_DB_PATH):
        shutil.copy2(_DB_PATH, backup_path)

    try:
        data = _fetch_json(_GITHUB_RAW_URL)
    except Exception as e:
        return json.dumps({"error": f"Download failed: {e}"}, ensure_ascii=False)

    if not isinstance(data, dict) or "_meta" not in data:
        return json.dumps(
            {"error": "The remote file has an invalid format because _meta is missing"},
            ensure_ascii=False,
        )

    local_data = _load_db()
    local_ver = local_data.get("_meta", {}).get("version", "0.0.0")
    local_updated = local_data.get("_meta", {}).get("updated", "")
    local_count = sum(
        1 for k, v in local_data.items()
        if k != "_meta" and isinstance(v, dict) and "latitude" in v
    )

    remote_ver = data.get("_meta", {}).get("version", "0.0.0")
    remote_updated = data.get("_meta", {}).get("updated", "")
    remote_count = sum(
        1 for k, v in data.items()
        if k != "_meta" and isinstance(v, dict) and "latitude" in v
    )

    _save_db(data)

Technical Analysis

The update-db command retrieves the database from the mutable main branch of a personal GitHub repository and overwrites the local trusted database. HTTPS protects the connection in transit, but it does not protect against compromise of the repository, maintainer account, workflow, or upstream branch.

Validation only confirms that the downloaded JSON is an object containing a _meta field. The implementation does not verify:

  • A cryptographic si ...[truncated 2005 chars]
Remediation
View remediation

Remediation Suggestions

  1. Fetch updates from an immutable release artifact or commit hash rather than a mutable branch.
  2. Publish a signed manifest and verify its signature before accepting an update.
  3. Pin and verify the expected SHA-256 digest of the downloaded database.
  4. Enforce a strict JSON schema covering _meta and every supported resort field.
  5. Reject unexpected fields, invalid URLs, non-finite coordinates, invalid types, and implausible elevation values.
  6. Apply a conservative response-size limit before parsing the body.
  7. Reject updates that unexpectedly remove or alter more than a configured percentage of records.
  8. Download to a temporary file, validate it completely, and then use an atomic rename to replace the database.
  9. Present the version, digest, source revision, additions, removals, and significant modifications for user confirmation before installation.
  10. Retain versioned backups and provide an automatic rollback mechanism if validation or subsequent loading fails.

T07 · Tool Hijacking and Spoofing

Warning
Location
tools/price_api.py:24
Finding

External FlyAI Executable Can Be Spoofed Through the Process PATH

Content
View full analysis

Vulnerability Details

File Location: tools/price_api.py:24-37
Vulnerability Type: Executable search-path hijacking
Risk Level: Medium

Vulnerable Code Snippet

python
def flyai_available() -> bool:
    try:
        r = subprocess.run(
            ["flyai", "--help"],
            capture_output=True,
            timeout=5,
        )
        return r.returncode == 0
    except Exception:
        return False


def run_flyai(args: list, timeout: int = 20) -> dict:
    try:
        r = subprocess.run(
            ["flyai"] + args,
            capture_output=True,
            text=True,
            timeout=timeout,
        )
        if r.returncode != 0:
            return {
                "error": f"flyai returned error code {r.returncode}",
                "stderr": r.stderr[:500],
            }
        data = json.loads(r.stdout)

Technical Analysis

The subprocess calls correctly use an argument vector and do not enable shell=True, so user-supplied travel parameters are not interpreted by a command shell. This prevents conventional shell metacharacter injection.

However, the executable is referenced only as flyai. The operating system therefore resolves it through the process's ambient PATH. The implementation does not verify the resolved executable's absolute path, ownership, permissions, signature, package origin, or cryptographic digest.

If an attacker can place an executable named flyai in a directory that appears earlier in PATH, the availability check and normal pricing calls will execute the attacker's program with the Agent process's privileges. The attacker does not need to exploit shell parsing because executable selection occurs before argument handling.

Attack Path

  1. An attacker obtains write access to a directory that appears before the legitimate FlyAI installation directory in the Agent's PATH.
  2. The attacker places a mal ...[truncated 1256 chars]
Remediation
View remediation

Remediation Suggestions

  1. Require an administrator-configured absolute path to the trusted FlyAI executable.
  2. Resolve the executable once with shutil.which, canonicalize it with os.path.realpath, and reject paths outside approved installation directories.
  3. Verify that the executable and its parent directories are not writable by untrusted users.
  4. Validate file ownership and permissions before every invocation or before caching the resolved path.
  5. Pin and verify a cryptographic digest or platform code signature for the trusted binary where operationally feasible.
  6. Run FlyAI with a minimal, explicitly constructed environment and a fixed safe PATH.
  7. Execute it under a sandbox with restricted filesystem and network access.
  8. Document the approved FlyAI package source, version, and installation procedure.
  9. Treat FlyAI output as untrusted data and continue enforcing strict JSON schema and size limits.
  10. Fail closed if executable identity verification cannot be completed.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (71)

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The README states that all data is stored locally only and never uploaded, but elsewhere advertises external searches and integrations for pricing, weather, and travel queries. That creates a misleading privacy/security representation: users may provide sensitive travel plans, images, or personal data believing nothing leaves the device, when requests may in fact be transmitted to third-party services.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description presents a broad ski assistant that helps users choose resorts, compare prices, monitor deals, check weather, and analyze technique. The supplied code instead is a narrowly scoped local card generator: it accepts JSON parameters, renders a score card image with Pillow, and saves it under ~/.ski-assistant/exports/. Two advertised card modes are explicitly unimplemented. There is no code for resort databases, pricing, weather, coaching inference, or deal tracking. While the card content is ski-themed and could be a supporting feature for sharing coaching results, the primary behavior of this code chunk is materially different from the declared purpose and adds an undeclared image-generation/file-writing capability.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a broad ski resort assistant focused on resort discovery, ski pricing, technique analysis, weather, and early-bird deal monitoring. The supplied code does none of those things. Its sole purpose is currency conversion using a public exchange-rate API with offline fallback values. While currency conversion could theoretically support ski trip budgeting, this code chunk by itself is not implementing ski-specific planning or pricing features and instead exposes a distinct, undeclared capability with a materially different primary purpose. Therefore this is a clear description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description is narrowly framed as a global ski assistant focused on ski resorts, lift tickets, ski coaching/form analysis, early-bird deals, and ski weather. The supplied code instead exposes generic travel-commerce search functions via an external CLI: search-flight, search-hotel, search-poi, and keyword-search. While some POI/keyword searches could be used for ski tickets or ski packages, the primary behavior here is broader travel search, including flights and hotels, which the description does not declare and even says it is not for non-ski travel. Additionally, claimed capabilities such as动作分析/form analysis and weather are not implemented in this code chunk. Therefore the code's actual behavior is materially different from the declared purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description presents a conversational ski assistant with user-facing capabilities like trip planning, price checks, action/form analysis, early-bird deal monitoring, and weather help. The supplied code chunk does something much narrower and different: it is an administrative/data-ingestion tool for resort discovery and database synchronization. It queries Overpass/Nominatim for ski resort locations, optionally enriches with elevation from Open-Meteo, and can write changes to a local JSON database or replace it from GitHub. Those are materially different primary behaviors from the declared assistant capabilities. While resort coverage/discovery is ski-related and partially aligned with 'find resorts,' the code lacks the advertised user features and introduces undeclared database update/write functionality and external data-sync behavior. Therefore this is a clear description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description presents a conversational ski assistant for consumers, covering resorts worldwide and offering recommendations, pricing, action analysis, weather, and deal monitoring. The supplied code instead is an internal maintenance script for local JSON strategy data. Its functions are limited to checking coverage/field quality, generating a markdown maintenance report, adding official channels, and manually reviewing WeChat channel records. This is materially different from the declared end-user assistant purpose. While the data relates to ski resorts, the actual behavior is administrative tooling rather than the described ski planning/coaching/deals assistant.

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

Claiming the skill responds naturally to casual Chinese or English requests with no special keywords creates a broad activation surface that can cause the agent to load this skill in unrelated conversations. Over-broad triggering is dangerous because it may invoke network access, local file reads/writes, or persistent data handling in contexts where the user did not intend to use the ski assistant, increasing privacy and integrity risk.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding

The skill uses 'env' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding

The skill uses 'shell' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The manifest describes a ski-focused assistant for resort selection, ski pricing, coaching/form analysis, early-bird deals, and ski weather. This file instead provides a standalone real-time currency conversion tool, which is not mentioned in the manifest and is outside the declared ski-only scope.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill manifest presents a ski-resort assistant, but this module exposes broad flight and hotel search capabilities that materially expand the agent's behavior beyond the declared scope. Scope mismatch is dangerous because it can enable unreviewed data flows and user actions that operators and users did not expect, especially when tied to external travel search services.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
83% confidence
Finding

The code can write to a local file via _save_db() and update_db(), modifying data/resorts_db.json when merge or update-db is invoked. In this ski-assistant context the write target is constrained to the skill's own data file, so this is not arbitrary filesystem compromise, but undeclared write capability can still create integrity and trust issues if the agent/runtime exposes this tool without clearly scoped permissions.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
83% confidence
Finding

The code can write to a local file via _save_db() and update_db(), modifying data/resorts_db.json when merge or update-db is invoked. In this ski-assistant context the write target is constrained to the skill's own data file, so this is not arbitrary filesystem compromise, but undeclared write capability can still create integrity and trust issues if the agent/runtime exposes this tool without clearly scoped permissions.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Lines L006-L016 state that v7.0.0 removed the tutorial subsystem and that tutorial functionality is no longer available. However, later README sections at L024-L062 describe tutorial features and specific tutorial files in present-tense release notes without clearly isolating them from current behavior, creating contradictory documentation about whether tutorials are part of the current skill.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Generic trigger terms like 'weather' and 'budget' overlap with ordinary conversation and may spuriously activate the skill outside its intended domain. While less severe than unrestricted natural-language triggering, this still risks accidental tool use, confusing routing, and unintended access to local persistence or web queries.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest presents the skill as an assistant for planning trips, comparing prices, coaching, weather, and early-bird deals. Adding a user-triggerable 'discover new resorts / update database' capability is an internal dataset-maintenance function rather than an obvious end-user ski-assistant feature.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest documents persistent storage of user data but provides no warning about privacy implications, retention, sharing boundaries, or how users can opt out. This is dangerous because users may unknowingly disclose travel plans, skill level, preferences, and usage history into durable local files that outlast the session and may be accessed later by other software or operators.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest limits scope to ski-resort assistance and explicitly excludes non-ski travel. However, the network request documentation states the skill performs real-time flight/hotel pricing via flyai CLI and web search, which is a broader travel-booking capability not clearly constrained to ski-specific itineraries.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The metadata declares the skill purpose and data type entirely in Chinese, framing the skill as guidance for Chinese-language search behavior without any indication that users can opt into another language or locale. Because SQP-3 applies to all file types and covers forced language or locale behavior, this constitutes a natural-language policy concern.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger list contains generic phrases such as “看看姿态”, “视频分析”, and “analyze my form”, which are broad enough to overlap with ordinary conversation outside this specific skiing-coach workflow. The file does not provide exclusion conditions or negative examples to clarify when the skill should not activate.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill requires persistent writes to ~/.ski-assistant/records.json containing dated performance history, highlights, and issues, but provides no explicit privacy notice, retention policy, or consent step. This creates undisclosed local data collection and persistence, which can expose sensitive behavioral or profile information to other local processes or users.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The module explicitly instructs the agent to execute a local Python script and to recommend installing Pillow. Even though the feature is ski-related marketing output, invoking local code and package installation expands the attack surface from content generation into code execution, which can be abused if the script or environment is modified, and it is not necessary for core coaching analysis.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The document is written entirely in Chinese and all required output formats, prompts, and user-facing suggestions are specified only in Chinese. There is no indication that users may choose another language or that the Chinese-only constraint is a documented, justified locale requirement.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger list includes broad phrases such as “发现新雪场”, “添加雪场”, and “update database”, which are generic enough to overlap with ordinary user requests rather than a narrowly scoped skill invocation. The file does not provide explicit negative examples or boundary conditions clarifying when these phrases should or should not activate the skill.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Allowing discovery results to be merged directly into the main resort database expands the skill from advisory use into modifying persistent application state. Even though records are marked _needs_review: true, automatic merge can still pollute trusted data with incorrect, duplicated, or maliciously manipulated upstream entries that later affect users or internal workflows.

Content

No source excerpt is available for this finding.