Back to skill

Security audit

A Stock Analysis 1.0.0

Security checks for vulnerabilities and agentic risk

Overview

This A-share stock analysis skill is coherent and disclosed, with local portfolio storage and Sina market-data requests that fit its stated purpose.

Before installing, be comfortable with storing portfolio holdings locally in a JSON file and with stock symbols being sent to Sina for quote analysis. On shared machines, restrict permissions on ~/.clawdbot/skills/a-stock-analysis/portfolio.json and consider backing it up before using update or remove commands.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/portfolio.py:24
Finding
Portfolio Data Is Written Without Explicitly Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/portfolio.py:24, 34-39` **Vulnerability Type**: Insecure storage of sensitive local data **Risk Level**: Medium ### Vulnerable Code ```python PORTFOLIO_FILE = Path.home() / ".clawdbot" / "skills" / "a-stock-analysis" / "portfolio.json" ``` ```python def save_portfolio(data: dict): """保存持仓数据""" PORTFOLIO_FILE.parent.mkdir(parents=True, exist_ok=True) data["updated_at"] = datetime.now().isoformat() with open(PORTFOLIO_FILE, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The portfolio file contains potentially sensitive financial information, including stock identifiers, acquisition costs, quantities, and timestamps. The application creates its storage directory and file without specifying restrictive permissions. The resulting permissions are inherited from the process umask. Under a common umask of `022`, the directory may be created with mode `0755` and the file with mode `0644`, allowing other local users to traverse the directory and read the portfolio data. The write operation also follows an existing symbolic link. If an attacker can write to the portfolio directory—for example, because it was previously created with unsafe ownership or permissions—the attacker could place a symbolic link at the portfolio path and cause subsequent saves to overwrite a file accessible to the victim account. ### Attack Path A local disclosure path is as follows: 1. The victim runs a portfolio command that invokes `save_portfolio`. 2. The application creates the directory and `portfolio.json` using permissions derived from the current umask. 3. With a permissive umask, the resulting directory and file are readable by other local accounts. 4. Another local user traverses the directory and reads `portfolio.json`. 5. The attacker obtains the victim's stock codes, position sizes, acquisition costs, and portfolio timestamps. A s ...[truncated 986 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the portfolio directory with mode `0700` and verify its ownership and permissions before use. 2. Create portfolio files with mode `0600`, independent of the process umask. 3. Write updates to a securely created temporary file in the same directory, flush and synchronize it, and atomically replace the destination with `os.replace`. 4. Refuse to operate on symbolic links and verify the destination with `lstat`. 5. Correct permissions on existing installations. 6. Handle write failures without leaving partially written portfolio data. Example hardening measures include: ```python PORTFOLIO_FILE.parent.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(PORTFOLIO_FILE.parent, 0o700) ``` For new files, use `os.open` with `O_CREAT | O_EXCL | O_WRONLY` and mode `0o600`, or use a secure temporary file followed by atomic replacement. Validate that the directory and destination are owned by the current user before writing. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/portfolio.py:149
Finding
Unvalidated Portfolio Values Cause Division-by-Zero Denial of Service<![CDATA[ ## Vulnerability Details **File Location**: `scripts/portfolio.py:149, 178, 211-212` **Vulnerability Type**: Missing numeric input validation **Risk Level**: Low ### Vulnerable Code ```python cost = pos["cost"] qty = pos["quantity"] cost_value = cost * qty market_value = realtime["price"] * qty pnl = market_value - cost_value pnl_pct = (realtime["price"] / cost - 1) * 100 ``` ```python print(f" 总盈亏: {total_value - total_cost:+.0f} 元 ({(total_value/total_cost-1)*100:+.2f}%)") ``` ```python add_parser.add_argument("--cost", "-c", type=float, required=True, help="成本价") add_parser.add_argument("--qty", "-q", type=int, required=True, help="持仓数量") ``` The update command similarly accepts unrestricted numeric values: ```python update_parser.add_argument("--cost", "-c", type=float, help="成本价") update_parser.add_argument("--qty", "-q", type=int, help="持仓数量") ``` ### Technical Analysis The argument parser enforces numeric types but does not enforce valid ranges. Consequently, zero and negative values are accepted for acquisition cost and quantity and are persisted to the portfolio file. When `cost` is zero, portfolio analysis evaluates: ```python realtime["price"] / cost ``` This raises `ZeroDivisionError` and terminates the command. A zero aggregate cost can also cause division by zero in the portfolio summary calculation. The application also trusts values loaded from `portfolio.json` without schema, type, range, or finiteness validation. Therefore, malformed values can enter through either the supported command-line interface or direct modification of the local JSON file. ### Attack Path A reliable command-line exploitation path is: 1. Add a position with a zero acquisition cost: ```bash uv run scripts/portfolio.py add 600789 --cost 0 --qty 1000 ``` 2. The application accepts and persists the invalid position. 3. Run portfolio analysis: ```bash uv run scripts/portfolio.py analyze ``` 4. If market data is retrieved succe ...[truncated 705 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require acquisition cost to be finite and strictly greater than zero. 2. Require quantity to be a positive integer. 3. Apply the same validation to both add and update operations. 4. Validate the complete schema of data loaded from `portfolio.json` before using it. 5. Reject `NaN`, positive infinity, and negative infinity by using `math.isfinite`. 6. Add defensive checks before percentage calculations and return a controlled validation error instead of raising an exception. 7. Report the affected stock code so users can repair invalid records without losing the rest of the analysis. For example: ```python import math def validate_position(cost: float, quantity: int) -> None: if not math.isfinite(cost) or cost <= 0: raise ValueError("Cost must be a finite number greater than zero") if quantity <= 0: raise ValueError("Quantity must be a positive integer") ``` Validation should occur before saving command-line values and again after loading persisted JSON because the file can be modified independently of the command-line interface. ]]>
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 (5)

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill explicitly documents commands to add, update, and remove holdings and later reveals that data is stored in `~/.clawdbot/skills/a-stock-analysis/portfolio.json`, but it does not clearly warn users up front that invoking these commands will modify persistent local state. In an agent setting, hidden filesystem writes are security-relevant because they can surprise users, alter financial records, or overwrite expected data without informed consent.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The analyze command iterates over the user's holdings and calls an external stock-analysis function for each stock code, which likely discloses portfolio constituents to a third-party data source or service. Even if only stock codes are transmitted, holdings data is sensitive financial information, and the script provides no explicit notice, consent prompt, or offline mode before sending it.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
文件中的名称说明、使用说明、输出示例和分析结论均默认以中文呈现,未说明这是可选语言,也未提供用户可选择其他语言或地区设置的方式。根据规则,强制特定语言而无用户选择属于自然语言层面的政策问题。

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
This code file contains natural-language descriptions, usage text, and help-facing documentation entirely in Chinese. Under the language/locale policy category, forcing a specific language without user opt-in or an explicit justification can be a policy violation.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
User-facing descriptions, help text, and printed output in this file are entirely in Chinese, and there is no indication that users can select another language or that the language restriction is an intentional, documented locale constraint. That creates a natural-language policy issue under the language/locale rule.

Static analysis

No suspicious patterns detected.