Back to skill

Security audit

hectorlee-daily-precision-picker

Security checks for vulnerabilities and agentic risk

Overview

This stock-screening skill has a coherent finance purpose, but its script can turn stock-code input into shell commands, so it needs manual review before installation.

Install only after the publisher fixes the command-injection path by validating stock codes and using shell-free subprocess argument lists. Also consider requiring explicit invocation, reviewing or pinning the npm dependency with integrity controls, and limiting JSON output paths. This is not evidence of malicious intent, but the current package can expose the user's local account if a crafted stock code reaches the script.

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

Error
Location
scripts/precision_picker.py:55
Finding
Shell Command Injection Through Unvalidated Stock Codes<![CDATA[ ## Vulnerability Details **File Location**: `scripts/precision_picker.py:55-60`, `scripts/precision_picker.py:154`, `scripts/precision_picker.py:217`, and `scripts/precision_picker.py:693-701` **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code Candidate codes are accepted from command-line arguments or a user-supplied file without format validation: ```python if args.pool: codes = [c.strip() for c in args.pool.split(",") if c.strip()] candidates = [{"code": c, "name": ""} for c in codes] elif args.pool_file: with open(args.pool_file) as f: for line in f: line = line.strip() if not line: continue parts = line.split() code = parts[0] name = parts[1] if len(parts) > 1 else "" candidates.append({"code": code, "name": name}) ``` The resulting values are directly interpolated into command strings: ```python cmd = f"npx -y westock-data-skillhub@1.0.5 finance {','.join(working_codes)} --num 1" stdout, rc, success = run_cmd(cmd, cwd=str(SKILL_DIR), timeout=90) ``` ```python code_list = ",".join(codes) cmd = f"npx -y westock-data-skillhub@1.0.5 fund flow {code_list}" stdout, rc, success = run_cmd(cmd, cwd=str(SKILL_DIR), timeout=90) ``` These command strings are executed through a shell: ```python def run_cmd(cmd, cwd=None, timeout=60): """Execute a command and return stdout and a success indicator.""" try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, timeout=timeout, cwd=cwd ) return result.stdout.strip(), result.returncode, True except subprocess.TimeoutExpired: return "", -1, False except Exception: return "", -1, False ``` ### Technical Analysis The code treats stock identifiers as trusted command fragments. No strict allowlist, regular-expression validation, argument escaping, or shell-free process i ...[truncated 2655 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Eliminate shell interpretation and pass arguments as a list: ```python result = subprocess.run( [ "npx", "-y", "westock-data-skillhub@1.0.5", "finance", ",".join(working_codes), "--num", "1" ], shell=False, capture_output=True, text=True, timeout=90, cwd=str(SKILL_DIR), check=False, ) ``` Apply the same change to the fund-flow invocation. 2. Validate every candidate before it reaches any processing layer. For the currently supported exchanges, use a strict allowlist such as: ```python import re STOCK_CODE_RE = re.compile(r"^(?:sh|sz)\d{6}$") def validate_stock_code(code): if not STOCK_CODE_RE.fullmatch(code): raise ValueError(f"Invalid stock code: {code!r}") return code ``` 3. Apply validation consistently to all input sources: - `--pool` - `--pool-file` - Automatically loaded VPS JSON signals - Any future API or scheduled-task input 4. Do not allow the Layer 1 offline fallback to bypass identifier validation. Syntax validation must occur before external market-data checks and must remain mandatory even when dependencies are unavailable. 5. Add security regression tests using candidates containing command separators, substitutions, redirects, whitespace, and newline characters. Verify that invalid values are rejected and that no shell is invoked. 6. Consider imposing limits on candidate count and input length to reduce denial-of-service and malformed-input risks. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/precision_picker.py:154
Finding
Runtime Download and Execution of an npm Package Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/precision_picker.py:154` and `scripts/precision_picker.py:217` **Vulnerability Type**: Runtime third-party dependency retrieval and execution **Risk Level**: Medium ### Vulnerable Code The financial-data path invokes an npm package through `npx`: ```python cmd = f"npx -y westock-data-skillhub@1.0.5 finance {','.join(working_codes)} --num 1" stdout, rc, success = run_cmd(cmd, cwd=str(SKILL_DIR), timeout=90) ``` The fund-flow path invokes the same package again: ```python code_list = ",".join(codes) cmd = f"npx -y westock-data-skillhub@1.0.5 fund flow {code_list}" stdout, rc, success = run_cmd(cmd, cwd=str(SKILL_DIR), timeout=90) ``` ### Technical Analysis `npx -y` can retrieve a package from the configured npm registry and execute its command-line entry point automatically. The package version is explicitly set to `1.0.5`, which reduces ordinary version drift, but the project does not provide a lockfile, vendored dependency, package-integrity hash, registry restriction, or audited installation step. The executed package is therefore outside the reviewed project artifact. Its effective behavior depends on the package delivered by the npm configuration and cache available at runtime. The `-y` option suppresses the interactive installation confirmation, so retrieval and execution occur without an explicit user approval step. This is a supply-chain exposure rather than evidence that the named package is malicious. The audit found no proof of malicious behavior in the package itself because its contents are not included in the reviewed project. ### Attack Path 1. A user invokes the stock-selection script with candidates that pass Layer 1. 2. Layer 2 constructs an `npx -y westock-data-skillhub@1.0.5` command. 3. If the package is not already available in an acceptable local cache, `npx` resolves it through the configured npm registry. 4. npm package installation behavior and the package’s CLI ent ...[truncated 1025 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Avoid downloading executable dependencies during normal Skill execution. Install and review the dependency as a separate deployment step. 2. Declare the package in a controlled npm project and commit an appropriate lockfile containing resolved package integrity information. 3. Use reproducible installation in deployment, such as `npm ci`, against a trusted and explicitly configured registry. 4. Verify the installed package and transitive dependency integrity before execution. Where operationally practical, mirror approved artifacts in an internal registry or vendor the audited implementation. 5. Disable dependency lifecycle scripts during installation when they are not required: ```bash npm ci --ignore-scripts ``` Confirm first that disabling scripts does not break legitimate package functionality. 6. Invoke the already installed executable directly rather than using `npx -y` at runtime. Fail closed with a clear dependency error when the approved executable is missing. 7. Run the external data tool with least privilege: - Use a restricted service account or sandbox. - Limit filesystem access. - Restrict outbound network destinations. - Avoid exposing unrelated credentials through environment variables. 8. Maintain a dependency-review and update process for the package and all transitive components, including vulnerability scanning and provenance verification. ]]>
Vulnerability Patterns
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (11)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def run_cmd(cmd, cwd=None, timeout=60):
    """执行命令并返回 stdout 和 success 标记"""
    try:
        result = subprocess.run(
            cmd, shell=True, capture_output=True, text=True,
            timeout=timeout, cwd=cwd
        )
Confidence
98% confidence
Finding
This is the core command-injection issue: shell=True turns the shell into an interpreter for attacker-controlled text, and later code interpolates joined stock codes directly into shell command strings. A malicious code like 'sh600519; rm -rf ~' or shell substitution payloads could execute arbitrary commands when the finance or fund-flow layers run. Because the skill supports user-provided pools and files, the attack surface is direct and practical.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill document includes explicit shell commands and describes file read/write behavior (such as generating JSON output and operating on local script directories) but declares no tool restrictions. In an agent environment, missing `permissions` or `allowed-tools` means the agent may execute broader capabilities than necessary, increasing the risk of unintended command execution, filesystem modification, or abuse if the skill is triggered in the wrong context.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger phrases include generic terms like '精选', '每日优选', '深度筛选', and '资金确认', which can easily overlap with ordinary user conversation. Overbroad triggers can cause the skill to activate unintentionally, and because this skill can drive shell execution and file operations, accidental invocation increases the chance of unnecessary external data access or command execution.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The manifest's description, tags, MCP requirement descriptions, and trigger phrases are entirely in Chinese, with no indication that users may choose another language or that the skill is intentionally restricted to a Chinese-language environment. This creates a natural-language locale constraint that is not presented as optional or explicitly justified.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger phrases include very generic financial terms such as '精选', '每日优选', and '深度筛选', which are likely to collide with ordinary user discussion rather than clear intent to invoke this specific skill. In an agent ecosystem, overly broad triggers can cause accidental activation, leading the skill to provide financial screening output when the user did not explicitly request this tool, which is especially sensitive in a finance context.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_cmd(cmd, cwd=None, timeout=60):
    """执行命令并返回 stdout 和 success 标记"""
    try:
        result = subprocess.run(
            cmd, shell=True, capture_output=True, text=True,
            timeout=timeout, cwd=cwd
        )
Confidence
97% confidence
Finding
The helper executes shell commands via subprocess.run with shell=True, and the command strings are built using unsanitized stock-code input in later layers. If an attacker supplies a crafted --pool or pool-file value containing shell metacharacters, arbitrary OS commands can execute under the user's account. In this skill context, the script is explicitly intended to process externally supplied candidate codes, which makes the sink realistically reachable.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The layer2_fundamental docstring says missing westockdata data should not be treated as a fake pass and should be clearly disclosed. However, when data is unavailable, the implementation appends every working code to the passed list and continues them through the funnel, only tagging them as skipped. That behavior contradicts the stated intent of not effectively passing unverifiable candidates.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The layer4_sector_quality documentation states that sector scoring is based on keyword classification from stock names. In the actual code, each stock's name is set to an empty string and _get_industry is called with that empty value, causing all entries to fall back to '其他' rather than using name-based classification. This is an active contradiction between the documented method and the implemented behavior.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The file title and all instructional content are written exclusively in Chinese, with no indication that language selection is optional or that the skill is region-specific. Under the policy, forcing a specific language without user opt-in can constitute a natural-language locale violation.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
SQP-3 applies to all file types and covers natural-language language/locale policy violations. The module docstring presents usage information entirely in Chinese, which effectively forces one language for users without opt-in or an explanation that the skill is region-specific.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
The CLI description, argument help strings, and error/usage output are all Chinese-only. Under SQP-3, this is a natural-language policy issue because the skill does not offer a language choice or clearly justify a locale-specific constraint.

Static analysis

No suspicious patterns detected.