Back to skill

Security audit

Skills of A-share market data released by ft.tech.

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a disclosed A-share market-data helper, but its dispatcher can be steered to execute unintended local Python handlers outside the bundled sub-skills.

Review this before installing. The market-data API behavior is disclosed and read-only, but the dispatcher should be fixed to allow only the named bundled sub-skills before it is trusted in an agent environment where user prompts or model decisions can influence command arguments.

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

Error
Location
run.py:35
Finding

Unrestricted Sub-Skill Path Allows Execution of Unintended Python Handlers

Content
View full analysis

Vulnerability Details

File Location: run.py:35-45
Vulnerability Type: Unvalidated path traversal leading to local code execution
Risk Level: High

python
subskill = sys.argv[1]
handler = os.path.join(SKILL_ROOT, "sub-skills", subskill, "scripts", "handler.py")

if not os.path.isfile(handler):
    print(f"错误:未找到 subskill '{subskill}',路径不存在:{handler}", file=sys.stderr)
    sys.exit(1)

result = subprocess.run(
    [sys.executable, handler] + sys.argv[2:],
    cwd=SKILL_ROOT,
)

Technical Analysis

The dispatcher directly incorporates the user-controlled subskill argument into a filesystem path and then executes the resulting file with the current Python interpreter. It only verifies that the resulting path references an existing file; it does not verify that the path remains inside the intended sub-skills directory or corresponds to one of the six bundled sub-skills.

Python's os.path.join() discards preceding components when a later component is absolute. Relative traversal components such as .. can also escape the intended directory after filesystem resolution. Therefore, a crafted value can cause handler to resolve to an unintended file matching the expected scripts/handler.py suffix.

The use of an argument array in subprocess.run() prevents shell metacharacter injection, but it does not prevent the path-selection vulnerability. The selected Python file is executed as code with the same permissions and environment as the dispatcher.

Attack Path

  1. An attacker identifies or creates a writable directory containing scripts/handler.py, such as /tmp/attacker-skill/scripts/handler.py.
  2. The attacker places arbitrary Python code in that handler.
  3. The attacker invokes the dispatcher with an absolute path:
    bash
    python run.py /tmp/attacker-skill
    
  4. os.path.join() resolves the handler to:
    text
    /tmp/attacker-skill/scripts/handler.py
    

...[truncated 1023 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace dynamic path construction with an explicit allowlist:

    python
    HANDLERS = {
        "block-trades": "sub-skills/block-trades/scripts/handler.py",
        "margin-trading-details": "sub-skills/margin-trading-details/scripts/handler.py",
        "stock-ipos": "sub-skills/stock-ipos/scripts/handler.py",
        "stock-list-all-stocks": "sub-skills/stock-list-all-stocks/scripts/handler.py",
        "stock-quotes-list": "sub-skills/stock-quotes-list/scripts/handler.py",
        "stock-security-info": "sub-skills/stock-security-info/scripts/handler.py",
    }
    
    relative_handler = HANDLERS.get(subskill)
    if relative_handler is None:
        print("Error: unsupported sub-skill", file=sys.stderr)
        sys.exit(1)
    
    handler = os.path.join(SKILL_ROOT, relative_handler)
    
  2. Apply defense-in-depth containment checks using canonical paths:

    python
    subskills_root = os.path.realpath(os.path.join(SKILL_ROOT, "sub-skills"))
    handler = os.path.realpath(handler)
    
    if os.path.commonpath([subskills_root, handler]) != subskills_root:
        print("Error: handler path escapes sub-skills directory", file=sys.stderr)
        sys.exit(1)
    
  3. Reject sub-skill values containing absolute paths, path separators, empty components, or .., even when an allowlist is used.

  4. Keep the existing argument-array form of subprocess.run() and do not introduce shell=True.

  5. Add negative tests covering absolute paths, ../ traversal, symlink escapes, unsupported names, and valid bundled sub-skills.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 18)May include surrounding context.

md
`run.py` 与本文件(`SKILL.md`)位于同一目录。执行时:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill instructs the agent to read local files, execute a Python entrypoint, and make outbound network requests, but it does not declare any explicit tool scope or permission boundaries. This increases the risk of over-broad agent execution, because a caller or downstream agent may invoke shell, file access, and network capabilities without clear policy constraints or user-visible authorization.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest description, operational instructions, and all user query examples are written entirely in Chinese, which signals the skill is intended to operate in a specific language without documenting any user opt-in or language-selection mechanism. The policy explicitly calls for flagging language or locale constraints when they are imposed without choice or justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The module docstring and CLI usage/error messages are written entirely in Chinese, which imposes a specific language on all users. The file does not indicate that Chinese is optional, configurable, or justified as a region-specific tool, so this is a natural-language locale policy concern.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · run.py (reported line 42)May include surrounding context.

python
print(f"错误:未找到 subskill '{subskill}',路径不存在:{handler}", file=sys.stderr)
        sys.exit(1)

    result = subprocess.run(
        [sys.executable, handler] + sys.argv[2:],
        cwd=SKILL_ROOT,
    )

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The markdown content presents all instructions and interface details only in Chinese, which can amount to a language-policy violation when the skill does not offer the user a language choice or explain that it is intentionally limited to a Chinese-speaking or region-specific audience. The file discusses A-share market data, but it does not explicitly state that the language restriction is a documented locale constraint or opt-in behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The natural-language strings in the module docstring and argument descriptions are entirely in Chinese, which indicates a fixed language experience without offering the user a choice or documenting a justified locale restriction. Under the policy, language constraints should be opt-in or explicitly justified as region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module docstring is written as a fixed Chinese-language description, "查询 A 股大宗交易列表," with no indication that language is configurable or user-selected. Under the stated policy, forcing a specific language without opt-in can be a natural-language policy violation unless the locale restriction is documented and justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

This markdown file presents all user-facing instructions exclusively in Chinese, including invocation guidance and parameter descriptions. Under the stated policy, forcing a specific language without user opt-in can be a natural-language policy violation unless the locale restriction is explicitly justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The module docstring is written only in Chinese ("获取所有 A 股股票代码列表"), which may impose a language choice without user opt-in. The file does not indicate that this is a justified region-specific or language-specific constraint, nor does it offer an alternative language option.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The script performs a network call to an external domain using urllib.request.urlopen and parses the response, but the file provides no explicit user-facing warning, confirmation, or comment about this outbound request. For code files, external network access can merit disclosure when there is no visible notice in the file itself.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This code constructs a URL using the user-provided symbol and sends it to the external domain ftai.chat. Although the script prints the response, it does not disclose before the request that input will be sent over the network, and there is no confirmation prompt or explanatory comment/docstring about this transmission.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.