Back to skill

Security audit

🍓 Fruit Pi · 水果派(全球水果·实时价格·水果价格)

Security checks for vulnerabilities and agentic risk

Overview

The skill’s fruit-price tracking purpose is coherent, but it can persist and repeatedly fetch unvalidated user-added or web-discovered URLs, creating avoidable local-file and internal-network access risk.

Review this skill before installing. It is not clearly malicious, but only add price-source URLs you trust, avoid internal or local URLs, and remove any unexpected sources from the fruit pool. Prefer using explicit commands like list or refresh rather than casual trigger phrases.

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

T09 · Insecure Skill Coding Practices

Warning
Location
fruit-pi.py:203
Finding

Unvalidated Price-Source URLs Enable SSRF and Local Resource Access

Content
View full analysis

Vulnerability Details

File Location: fruit-pi.py:203-218, fruit-pi.py:320-329, fruit-pi.py:432-450, and fruit-pi.py:554-562
Vulnerability Type: Server-Side Request Forgery (SSRF) and unsafe URL scheme handling
Risk Level: Medium

Vulnerable Code

python
def fetch_url(url, timeout=10):
    """Fetch URL content with basic headers."""
    try:
        req = urllib.request.Request(
            url,
            headers={
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                              "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
                "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
            }
        )
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            html = resp.read().decode("utf-8", errors="replace")
            return html
    except Exception as e:
        log(f"fetch_url failed for {url}: {e}")
        return None
python
for src in sources:
    url = src.get("url", "")
    source_name = src.get("name", "")
    currency = src.get("currency", "CNY")

    if not url:
        results["errors"].append(f"{source_name}: 未配置URL")
        continue

    html = fetch_url(url)
python
def add_source(pool, fruit_name, source_name, url, currency="CNY"):
    """Add a price source to an existing fruit."""
    fruits = pool.get("fruits", {})
    if fruit_name not in fruits:
        return {"error": f"{fruit_name} 不在水果池中,请先 --add"}
    
    sources = fruits[fruit_name].setdefault("sources", [])
    for s in sources:
        if s.get("name") == source_name:
            s["url"] = url
            s["currency"] = currency
            save_fruit_pool(pool)
            return {"message": f"✅ {fruit_name} 的报价来源已更新: {source_name}"}
    
    source
...[truncated 3495 chars]
Remediation
View remediation

Remediation Suggestions

  1. Parse every source with urllib.parse.urlsplit and allow only the https scheme. Reject URLs containing credentials, malformed hosts, or unsupported ports.
  2. Prefer a strict allowlist of approved fruit-price domains. If arbitrary public domains are required, require explicit user confirmation before the first request to each new domain.
  3. Resolve all destination hostnames before connecting. Use the ipaddress module to reject loopback, private, link-local, multicast, reserved, and unspecified IPv4 and IPv6 addresses.
  4. Explicitly block cloud metadata hostnames and addresses, including link-local metadata endpoints.
  5. Disable automatic redirects or validate the scheme, hostname, port, and resolved IP address of every redirect target before following it.
  6. Protect against DNS rebinding by ensuring that the validated address is the address used for the connection, or route outbound requests through a controlled proxy with equivalent destination restrictions.
  7. Apply a response-size limit and reject unexpected content types before reading or parsing the complete response.
  8. Use short connection and read timeouts and limit the number of redirects.
  9. Validate URLs both when they are added and immediately before each request, because persisted state may be modified outside the CLI.
  10. Add automated tests covering loopback, RFC 1918 private ranges, IPv6 local ranges, link-local metadata addresses, alternate numeric IP representations, file: URLs, embedded credentials, and redirects from an allowed public host to a blocked destination.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (10)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill invokes file reads/writes, network fetching, and shell execution but does not declare any explicit tool scope or permission boundaries. This weakens platform enforcement and user transparency, increasing the chance of unintended filesystem modification, outbound requests, or command execution when the skill is triggered.

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 everyday phrases such as fruit-related terms that users may say conversationally, causing accidental activation. Because activation can lead to file creation, modification, shell execution, and network collection, an unintended trigger has real side effects beyond just producing a response.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation says the skill executes immediately on any listed trigger, with no contextual validation or confirmation step. In this skill, trigger-on-mention is more dangerous because it can automatically create persistent state and start network or shell-based collection workflows without a deliberate user request.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill automatically creates and updates a persistent fruit-pool file but does not prominently warn users in the description or activation flow. Hidden persistence can surprise users, create unwanted state, and make later skill behavior depend on silently stored data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill performs web search, URL fetching, and external data collection but does not provide a clear privacy or network disclosure. Users may not realize that their requested fruit interests or configured sources can drive outbound requests to third-party services, exposing usage patterns or causing untrusted content retrieval.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · fruit-pi.py (reported line 40)May include surrounding context.

python
# Exchange rate cache (1h TTL)
EXCHANGE_RATE_CACHE_PATH = os.path.join(SCRIPT_DIR, ".rate_cache.json")
EXCHANGE_RATE_API = "https://api.frankfurter.app/latest?from=USD"
EXCHANGE_RATE_CACHE_TTL = 3600  # 1 hour

# ─── Currency Config ──────────────────────────────────────────────────

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · fruit-pi.py (reported line 117)May include surrounding context.

python
# Exchange rate cache (1h TTL)
EXCHANGE_RATE_CACHE_PATH = os.path.join(SCRIPT_DIR, ".rate_cache.json")
EXCHANGE_RATE_API = "https://api.frankfurter.app/latest?from=USD"
EXCHANGE_RATE_CACHE_TTL = 3600  # 1 hour

# ─── Currency Config ──────────────────────────────────────────────────

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The request header forces Accept-Language: zh-CN,zh;q=0.9,en;q=0.8, which imposes a specific language/locale preference in network interactions. This is a natural-language policy issue because the file provides no user opt-in, configuration, or justification for enforcing a Chinese locale.

Content

No source excerpt is available for this finding.

Overly Broad Trigger

Low
Category
Trigger Abuse
Confidence
84% confidence
Finding

The trigger '果价' is short and generic, making accidental matches more likely in normal conversation. In this skill, even a low-specificity trigger matters because activation can cascade into shell execution, network access, and persistent file changes.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The operational instructions are framed around Chinese trigger phrases and the sample interaction/output is primarily Chinese, but the document does not state that users may choose another language for interaction. Because the skill also presents an English description, the lack of an explicit language choice may conflict with a language/locale flexibility policy.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.