Back to skill

Security audit

Tpvl

Security checks for vulnerabilities and agentic risk

Overview

This TPVL skill coherently fetches public volleyball data, with ordinary dependency and cache-hardening issues but no evidence of hidden or malicious behavior.

Reasonable to install for TPVL lookups. Run it as an ordinary user, be aware that uv may install current package versions unless the publisher adds pins or a lock file, and treat cached results as non-authoritative if the local /tmp directory is shared with untrusted users.

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

T08 · Insecure Dependencies

Warning
Location
scripts/_tpvl_api.py:2
Finding

Unpinned Dependencies Are Automatically Resolved at Runtime

Content
View full analysis

Vulnerability Details

File Location: scripts/_tpvl_api.py:2-9 (also repeated in each executable script)
Vulnerability Type: Unpinned runtime dependencies
Risk Level: Medium

Vulnerable Code

python
# /// script
# requires-python = ">=3.11"
# dependencies = [
#     "requests",
#     "beautifulsoup4",
#     "lxml",
# ]
# ///

The same unpinned dependency declarations are present in:

  • scripts/tpvl_games.py:2-9
  • scripts/tpvl_schedule.py:2-9
  • scripts/tpvl_standings.py:2-9
  • scripts/tpvl_stats.py:2-9

SKILL.md:95-101 explicitly states that these packages are automatically installed through uv.

Technical Analysis

All third-party packages are specified without exact versions or cryptographic hashes. Consequently, each environment may resolve and execute a different dependency version depending on the package index state and resolver behavior at installation time.

This creates a supply-chain exposure: compromise of a declared package, a malicious future release, or an incompatible release could introduce arbitrary code into the environment. Python packages can execute code during installation or whenever imported. In this project, requests is imported directly by scripts/_tpvl_api.py, so malicious package code would run with the privileges of the user invoking the skill.

No evidence was found that the currently named packages are malicious. The vulnerability is the absence of reproducible, integrity-verified dependency resolution.

Attack Path

  1. An attacker compromises the account or release process of one of the declared packages, or otherwise influences the package source used by uv.
  2. The attacker publishes a malicious version that still satisfies the unconstrained dependency declaration.
  3. A user runs one of the scripts through uv run.
  4. uv resolves and installs the attacker-controlled package version.
  5. Malicious code executes during in ...[truncated 656 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin every direct dependency to an audited exact version rather than using unconstrained names.
  2. Generate and commit a lock file containing resolved transitive versions.
  3. Require package hashes where supported so altered distributions are rejected.
  4. Configure uv to use an explicitly trusted package index and avoid unreviewed fallback indexes.
  5. Add automated dependency vulnerability and provenance scanning.
  6. Upgrade dependencies through a controlled review process rather than resolving the newest available release during ordinary skill execution.
  7. Remove unused dependencies. The reviewed implementation does not currently use beautifulsoup4 or lxml, so retaining them unnecessarily increases the supply-chain attack surface.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/_tpvl_api.py:25
Finding

Predictable Shared Temporary Cache Permits Cache Poisoning and Symlink File Writes

Content
View full analysis

Vulnerability Details

File Location: scripts/_tpvl_api.py:25-26, 62-93
Vulnerability Type: Unsafe predictable files in a shared temporary directory
Risk Level: Medium

Vulnerable Code

python
# 快取設定
CACHE_DIR = Path('/tmp/tpvl_cache')
CACHE_TTL = timedelta(minutes=30)

def _get_cache_path(key: str) -> Path:
    """取得快取檔案路徑"""
    CACHE_DIR.mkdir(parents=True, exist_ok=True)
    return CACHE_DIR / f'{key}.json'


def _load_cache(key: str) -> Optional[dict]:
    """載入快取資料"""
    path = _get_cache_path(key)
    if not path.exists():
        return None
    try:
        data = json.loads(path.read_text())
        cached_at_str = data.get('_cached_at', '')
        if not cached_at_str:
            return None
        cached_at = datetime.fromisoformat(cached_at_str)
        # 確保時區一致比較
        now = datetime.now(tz=TST)
        if cached_at.tzinfo is None:
            cached_at = cached_at.replace(tzinfo=TST)
        if now - cached_at < CACHE_TTL:
            return data
    except (ValueError, OSError) as exc:
        logger.debug('快取讀取失敗 (%s): %s', key, exc)
    return None


def _save_cache(key: str, data: dict):
    """儲存快取資料"""
    data['_cached_at'] = datetime.now(tz=TST).isoformat()
    _get_cache_path(key).write_text(json.dumps(data, ensure_ascii=False))

Technical Analysis

The cache uses a global, predictable path under the shared /tmp namespace. The directory is created without verifying its owner, permissions, or file type. Cache files also have predictable names such as homepage.json, schedule.json, and record.json.

_load_cache() trusts any syntactically valid JSON containing a sufficiently recent _cached_at value. It does not authenticate the cached content or confirm that the file is owned by the current user. A local user able to create or modify the shared cache can therefore inject fabricated match and standings information ...[truncated 2574 chars]

Remediation
View remediation

Remediation Suggestions

  1. Store cache data in a per-user cache directory, such as one derived from platformdirs.user_cache_dir(), rather than a globally shared fixed path.
  2. If temporary storage is required, create a private directory with mode 0700 and verify that it is owned by the effective user.
  3. Reject cache directories and files that are symbolic links or have unexpected owners or permission modes.
  4. Open cache files with no-follow semantics where supported, and validate the opened file with fstat() before reading or writing.
  5. Write to a securely created temporary file in the same private directory, flush it, and atomically replace the final cache file.
  6. Apply restrictive file permissions such as 0600.
  7. Treat cached content as untrusted. Validate its expected schema, field types, timestamp bounds, and maximum size before use.
  8. Do not accept timestamps unreasonably far in the future.
  9. Where integrity against local tampering is required, authenticate cached data or avoid cross-process cache sharing entirely.
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description says the skill covers TPVL stats, scores, schedules, and standings. The supplied code chunk only implements player statistics retrieval from the TPVL website, including team alias resolution, category filtering, sorting, and output formatting. There is no functionality here for match scores, schedules, or league standings. That makes the description materially broader than the behavior shown in this code chunk. No suspicious unrelated or undeclared capabilities are evident beyond this scope mismatch.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill documentation indicates capabilities that involve network access and local caching to /tmp, but it does not declare any explicit tool scope or permissions. Missing scope declarations weaken least-privilege controls and can allow an agent runtime to grant broader file or network access than users expect, which is risky even in a sports-data skill.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The README includes substantial Chinese-language instructions and labels alongside English, but does not state the supported language(s) or let users choose their preferred language. This can violate a language/locale policy when users are not given an opt-in or clear expectation about the required language.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The file is written entirely in Traditional Chinese and does not indicate that users may choose another language or that the document is intentionally limited to a specific locale or audience. Under the stated policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The HTTP header explicitly sets Accept-Language to prefer zh-TW, and the module’s user-facing strings and time conversion are tailored to Taiwan. Under the policy, forcing a specific language/locale without offering user choice or documenting a justified region-specific constraint is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script's natural-language interface, including the module docstring, argparse description, help text, examples, and runtime messages, is entirely in Traditional Chinese. Under the stated policy, forcing a specific language without offering user opt-in or documenting a justified region-specific constraint is a policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code file contains natural-language strings such as the module docstring, CLI description, help text, and output labels entirely in Traditional Chinese. Under the policy rule, forcing a specific language without user opt-in is a locale/language policy issue unless the constraint is explicitly justified as region-specific.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.