Back to skill

Security audit

Stockstar Fund Rating - 证券之星基金评级

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed public fund-rating lookup tool that fetches StockStar rating pages and keeps a local cache, with minor scoping and cache-file hardening notes.

Install this only if you want a Chinese-language public fund-rating lookup that may contact fund.stockstar.com and maintain a local ratings cache. Prefer running it in a normal least-privilege agent environment, and avoid installing it in a shared writable skill directory until the cache temp-file handling and trigger wording are tightened.

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
scripts/cache.py:87
Finding

Predictable Temporary Cache File Enables Symlink-Based File Overwrite

Content
View full analysis

Vulnerability Details

File Location: scripts/cache.py, lines 87–93
Vulnerability Type: Predictable temporary file with unsafe symlink handling
Risk Level: Medium

python
global _CACHE
_CACHE = cache
path = _cache_path()
tmp = path + ".tmp"
with open(tmp, "w", encoding="utf-8") as f:
    json.dump(cache, f, ensure_ascii=False, separators=(",", ":"))
os.replace(tmp, path)

Technical Analysis

The cache writer derives a predictable temporary filename, fund_cache.json.tmp, and opens it with ordinary write mode. This operation follows symbolic links and truncates the linked target before writing JSON data. It does not use exclusive creation, O_NOFOLLOW, or a securely randomized temporary filename.

The final os.replace() provides atomic replacement for the cache destination, but it does not protect the preceding open() operation. Therefore, a local attacker who can create or replace files in the scripts directory can point the temporary path at another file writable by the Agent account.

Attack Path

  1. A local attacker obtains write access to the project’s scripts directory.
  2. The attacker creates scripts/fund_cache.json.tmp as a symbolic link to a target file writable by the account running the Skill.
  3. The victim invokes a query that builds, updates, or appends data to the cache.
  4. save() opens the predictable temporary path in write mode and follows the symbolic link.
  5. The target file is truncated and overwritten with cache JSON.
  6. os.replace() then attempts to move the symlink path to the cache destination; regardless of that final result, the linked target has already been modified.

Impact Assessment

Exploitation requires local write access to the Skill directory and does not independently provide privilege escalation. If that prerequisite is met, the attacker can corrupt or overwrite an arbitrary file that is writable by the Agent process.

The resu ...[truncated 399 chars]

Remediation
View remediation

Remediation Suggestions

Create the temporary file securely and atomically in the same directory as the destination:

  1. Use tempfile.NamedTemporaryFile() or tempfile.mkstemp() with dir=os.path.dirname(path) and a randomized filename.
  2. Apply restrictive permissions, such as mode 0o600.
  3. Do not follow symbolic links. If using os.open(), combine O_CREAT, O_EXCL, and O_NOFOLLOW where supported.
  4. Write and flush the JSON content, then call os.fsync() when durability is required.
  5. Atomically replace the destination using os.replace().
  6. Remove the temporary file in a finally block if any operation fails.
  7. Ensure the cache directory is not writable by untrusted users.

Example hardening pattern:

python
import json
import os
import tempfile

def save(cache):
    global _CACHE
    path = _cache_path()
    directory = os.path.dirname(path) or "."
    tmp_path = None

    try:
        fd, tmp_path = tempfile.mkstemp(
            dir=directory,
            prefix=".fund_cache.",
            suffix=".tmp",
        )
        os.chmod(tmp_path, 0o600)

        with os.fdopen(fd, "w", encoding="utf-8") as f:
            json.dump(cache, f, ensure_ascii=False, separators=(",", ":"))
            f.flush()
            os.fsync(f.fileno())

        os.replace(tmp_path, path)
        tmp_path = None
        _CACHE = cache
    finally:
        if tmp_path is not None:
            try:
                os.unlink(tmp_path)
            except FileNotFoundError:
                pass
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
Findings (6)

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding

Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill invokes a Python script that likely uses network access and may read/write local cache data, yet the manifest does not declare any tool scope or allowed-tools boundaries. Without explicit capability scoping, an agent runtime may grant broader-than-necessary access, increasing blast radius if the script is modified, compromised, or misused.

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 terms like '基金' and '评级', which can cause the skill to activate on loosely related conversations. Overbroad activation can route users into unintended code execution and network access paths, creating unnecessary exposure and increasing the chance of irrelevant or misleading responses.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The activation guidance says to trigger whenever users mention broad topic categories around fund ratings, which is ambiguous and may over-fire. In a skill that executes scripts and fetches remote data, ambiguous activation expands the operational surface and can produce unintended external requests or context hijacking within adjacent finance discussions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The workflow explicitly instructs the AI to respond '用中文向用户总结', which imposes a fixed language requirement. There is no opt-in, user choice, or documented justification that this skill must only operate in Chinese, so this is a language/locale policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

This Python file contains its primary module documentation entirely in Chinese and later emits user-facing stderr status messages in Chinese. Under the stated policy, forcing a specific language without user opt-in is a natural-language locale violation unless the constraint is explicitly justified or optional.

Content

No source excerpt is available for this finding.