Back to skill

Security audit

Chan Signal

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Chinese A-share technical-analysis skill, with some disclosed network use and fixable local-cache/dependency hygiene risks.

Install only in an isolated Python environment, avoid running it with elevated privileges, and treat the generated trading output as informational. The publisher should pin dependencies, validate stock codes as exactly six digits, harden or disable the temp cache, and clearly disclose the quote-server network calls.

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)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/chan_signal.py:62
Finding

Path Traversal and Unsafe Shared Temporary Cache Handling

Content
View full analysis

Vulnerability Details

File Location: scripts/chan_signal.py, lines 62-63 and 90-105, with the unsafe write at lines 172-177
Vulnerability Type: Path traversal, unsafe temporary-file handling, and potential symbolic-link abuse
Risk Level: Medium

Vulnerable Code

python
def _normalize_code(code: str) -> str:
    return str(code).strip().zfill(6)
python
code = _normalize_code(code)
full_code, market = _code_to_market(code)

# Try cache
cache_path = os.path.join(CACHE_DIR, str(category), f"{code}.csv")
disk_cached = pd.DataFrame()
if os.path.isfile(cache_path):
    try:
        disk_cached = pd.read_csv(cache_path, encoding="utf-8-sig")
        if "date" in disk_cached.columns:
            disk_cached["date"] = pd.to_datetime(disk_cached["date"], errors="coerce")
            disk_cached = disk_cached.dropna(subset=["date"])
        for c in ("open", "close", "high", "low", "vol", "amount"):
            if c in disk_cached.columns:
                disk_cached[c] = pd.to_numeric(disk_cached[c], errors="coerce").fillna(0)
        disk_cached = disk_cached.sort_values("date").reset_index(drop=True)
        if len(disk_cached) >= count:
            return disk_cached.iloc[-count:].copy()
    except Exception:
        pass
python
# Save cache
try:
    os.makedirs(os.path.dirname(cache_path), exist_ok=True)
    df.to_csv(cache_path, index=False, encoding="utf-8-sig")
except Exception:
    pass

Technical Analysis

The stock code is documented as a six-digit numeric value, but _normalize_code() only strips whitespace and left-pads short values. It does not verify that the value contains exactly six digits or reject absolute paths, directory separators, or .. traversal components.

The normalized value is interpolated directly into a file name and passed to os.path.join(). A traversal sequence can escape the intended cache directory. If the resulting component is absolute, Python's path joining behavior can discard the ...[truncated 2389 chars]

Remediation
View remediation

Remediation Suggestions

  1. Enforce the documented stock-code format before performing any network or filesystem operation:
python
import re

def _normalize_code(code: str) -> str:
    normalized = str(code).strip()
    if not re.fullmatch(r"\d{6}", normalized):
        raise ValueError("Stock code must contain exactly six digits")
    return normalized
  1. Validate category, count, and recent_bars against explicit allowlists and safe numeric ranges.

  2. Construct and canonicalize the cache path, then verify containment beneath the canonical cache root:

python
cache_root = os.path.realpath(CACHE_DIR)
cache_path = os.path.realpath(
    os.path.join(cache_root, str(category), f"{code}.csv")
)

if os.path.commonpath([cache_root, cache_path]) != cache_root:
    raise ValueError("Invalid cache path")
  1. Create the cache root as a per-user private directory with restrictive permissions, such as mode 0700, rather than relying on a broadly shared predictable temporary path.

  2. Reject symbolic links for cache files and parent directories. Where supported, use descriptor-based APIs with O_NOFOLLOW and exclusive creation semantics.

  3. Write to a safely created temporary file in the same directory, flush and synchronize it, and atomically replace the destination only after successful serialization.

  4. Verify cache file ownership and permissions before reading it. Treat cache content as untrusted and enforce schema, row-count, date-range, numeric-range, and maximum-file-size limits.

  5. Log cache validation and persistence failures instead of silently suppressing every exception.

T08 · Insecure Dependencies

Note
Location
SKILL.md:16
Finding

Unpinned Third-Party Dependency Installation

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 16-20
Vulnerability Type: Unpinned and non-reproducible third-party dependency installation
Risk Level: Low

Vulnerable Code

bash
pip install pytdx pandas numpy

Technical Analysis

The installation instructions request mutable package names without version constraints, integrity hashes, a lock file, or an explicitly reviewed package source. The effective code installed by this command can therefore change over time even when the audited project remains unchanged.

Python packages can execute code during installation and later when imported. If the configured package index, a dependency artifact, a maintainer account, or a future package release is compromised, following these instructions could introduce unreviewed code into the environment.

The package names shown are not apparent typosquatting names, and the audited project contains no evidence that the author intentionally directs users to a malicious repository. The issue is therefore an avoidable supply-chain weakness rather than confirmed malicious dependency behavior.

Attack Path

  1. A user follows the prerequisite instruction and runs the unpinned pip install command.
  2. pip resolves the latest versions available from the user's configured package index at installation time.
  3. A compromised or unexpectedly modified direct or transitive package artifact is downloaded.
  4. Package-controlled code may execute during installation or when pytdx, pandas, or numpy is imported.
  5. That code runs with the permissions of the user or automation account performing installation or analysis.

This path requires compromise or unsafe configuration of the package supply chain; the repository itself does not contain evidence of such a compromise.

Impact Assessment

If dependency resolution supplies a malicious artifact, it could execute arbitrary code with the privileges of the installing user. Depending on those p ...[truncated 365 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin all direct and transitive dependencies to reviewed versions in a lock file.
  2. Record package hashes and install with integrity enforcement, for example:
bash
python -m pip install --require-hashes -r requirements.txt
  1. Generate the requirements file through a reproducible dependency-management workflow and review dependency updates before merging them.
  2. Install dependencies inside an isolated virtual environment using a non-privileged account.
  3. Use a trusted, explicitly configured package index or an internally controlled artifact repository.
  4. Add automated vulnerability and provenance scanning for direct and transitive dependencies.
  5. Avoid instructing users to run package installation with administrator or root privileges.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (4)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest description and main overview are entirely written as Chinese-only instructions and present the skill as operating in Chinese, with no indication that users may choose another language. This is a natural-language locale policy concern because the file imposes a specific language experience without explicit opt-in or a documented region-specific justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script's descriptive text and generated output fields are written in Chinese, and the tool does not offer any language or locale selection for users. This creates a language-policy concern because the skill effectively forces a specific language without user opt-in.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script connects to multiple external quote servers and retrieves market data without any user-facing disclosure that an outbound network connection will occur. While this is expected for a market-analysis tool and does not itself indicate malicious behavior, it can still expose user activity metadata and violate least-surprise or privacy expectations in constrained environments.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script writes fetched market data to a temp-directory cache on local disk without informing the user. This can leave behind artifacts about what symbols were analyzed and when, which is a privacy and operational-hygiene concern on shared systems even though the cached data is not highly sensitive by itself.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.