Back to skill

Security audit

Dividend Premium Tracker

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent market-data tracker, but it has under-disclosed external alerting and unsafe command execution patterns that need review before installation.

Review before installing. Do not configure TELEGRAM_BOT_TOKEN or cron until the Telegram chat ID is user-configurable, os.system curl calls are replaced with safe HTTP calls, and output paths are moved to a user-selected or skill-owned directory. Expect the skill to download Chinese market data and overwrite CSV/Excel report files when run.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/monitor_dividend_premium.py:21
Finding
Shell Command Injection in Telegram Alert Delivery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/monitor_dividend_premium.py`, lines 21–36 **Vulnerability Type**: OS command injection through unsafe shell command construction **Risk Level**: High ### Vulnerable Code ```python TELEGRAM_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "") TELEGRAM_CHAT_ID = "505395883" def send_telegram(message): """发送Telegram消息""" if not TELEGRAM_TOKEN: print("未配置Telegram Bot Token") return False cmd = f"""curl -s -X POST https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage \ -d chat_id={TELEGRAM_CHAT_ID} \ -d text="{message}" \ -d parse_mode=HTML""" os.system(cmd) return True ``` ### Technical Analysis The function embeds `TELEGRAM_TOKEN` and `message` directly into a shell command and executes the result with `os.system()`. The shell interprets command separators, substitutions, redirections, and quoting characters contained in these values. The token is read from the process environment. Alert messages also include values derived from the writable CSV data set. Consequently, an attacker who can influence either source can introduce shell syntax that escapes the intended `curl` command. Using HTTPS does not mitigate this issue because command injection occurs locally before `curl` sends the request. Execution inherits the environment, filesystem access, and operating-system privileges of the account running the Skill. ### Attack Path 1. An attacker gains the ability to control `TELEGRAM_BOT_TOKEN` or modify a value in the CSV file read by the monitoring script. 2. The attacker inserts shell metacharacters or command-substitution syntax into the controlled value. 3. An alert condition is triggered, such as a premium below the configured threshold. 4. `send_telegram()` interpolates the malicious value into `cmd`. 5. `os.system(cmd)` launches a shell and interprets the injected syntax. 6. The injected command executes with the privileges of the us ...[truncated 705 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `os.system()` and avoid constructing commands through string interpolation. - Prefer a Python HTTPS client such as `urllib.request` or `requests`. - If `curl` must be used, call `subprocess.run()` with a fixed argument list and `shell=False`, for example: ```python subprocess.run( [ "curl", "-sS", "-X", "POST", f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage", "--data-urlencode", f"chat_id={TELEGRAM_CHAT_ID}", "--data-urlencode", f"text={message}", "--data-urlencode", "parse_mode=HTML", ], check=True, timeout=30, ) ``` - Validate the Telegram token against the expected token format before use. - Treat all CSV fields as untrusted input and validate dates and numeric values before including them in messages. - Apply request timeouts and check response status rather than returning success unconditionally. - Do not configure unattended cron execution until the shell-injection path has been removed. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/update_dividend_premium.py:19
Finding
Hard-Coded Writes Outside the Skill Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/update_dividend_premium.py`, lines 19–21; duplicated in `scripts/monitor_dividend_premium.py`, lines 18–20 **Vulnerability Type**: Excessive filesystem scope and unsafe hard-coded workspace access **Risk Level**: Medium ### Vulnerable Code ```python DATA_DIR = "/Users/liyi/.openclaw/workspace" CSV_FILE = os.path.join(DATA_DIR, "股息率溢价跟踪.csv") EXCEL_FILE = os.path.join(DATA_DIR, "股息率溢价跟踪.xlsx") ``` The same configuration appears in the monitoring script: ```python DATA_DIR = "/Users/liyi/.openclaw/workspace" CSV_FILE = os.path.join(DATA_DIR, "股息率溢价跟踪.csv") EXCEL_FILE = os.path.join(DATA_DIR, "股息率溢价跟踪.xlsx") ``` ### Technical Analysis Both scripts access a fixed OpenClaw workspace path instead of a Skill-owned output directory or a location explicitly selected by the user. The update script rewrites the CSV file and generates an Excel file at this location. The monitoring script reads and may rewrite the same CSV file. Market-report generation does not require unrestricted access to the broader Agent workspace. This hard-coded path therefore exceeds the minimum filesystem scope necessary for the declared functionality and is not disclosed in the setup instructions. The behavior does not bypass operating-system permissions or provide privilege elevation. However, when the Skill runs under an account that can write to this directory, it can modify workspace state outside its own project boundary. ### Attack Path 1. The Skill is executed under a user account that has access to `/Users/liyi/.openclaw/workspace`. 2. Existing files with the hard-coded names are loaded as Skill data. 3. The update or monitoring flow rewrites those files without confirming that they belong to this Skill. 4. Existing workspace content may be replaced, or untrusted preexisting CSV content may enter subsequent processing. 5. In combination with the Telegram shell-injection issue, malicious values placed in the external C ...[truncated 573 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Store output under a dedicated directory owned by the Skill, or use a standard per-user application-data directory. - Support an explicit `--data-dir` argument or a documented environment variable instead of embedding a developer-specific absolute path. - Resolve and validate the selected path before writing, and reject paths outside an approved base directory when sandboxing is expected. - Create the directory with restrictive permissions where appropriate. - Before overwriting an existing file, verify that it is a regular file owned by the expected user and belongs to this Skill. - Use atomic writes: create a temporary file in the destination directory, flush and validate it, and then replace the destination. - Document all files the Skill reads and writes so users can make an informed permission decision. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/update_dividend_premium.py:24
Finding
Predictable Shared Temporary File Enables Symlink and Race Attacks<![CDATA[ ## Vulnerability Details **File Location**: `scripts/update_dividend_premium.py`, lines 24–42; equivalent behavior in `scripts/monitor_dividend_premium.py`, lines 40–53 **Vulnerability Type**: Unsafe temporary-file creation **Risk Level**: Medium ### Vulnerable Code ```python def download_dividend_rate(date_str): """从中证指数官网下载股息率数据""" url = "https://oss-ch.csindex.com.cn/static/html/csindex/public/uploads/file/autofile/indicator/H30269indicator.xls" local_file = "/tmp/H30269indicator.xls" os.system(f"curl -s -o {local_file} '{url}'") try: book = xlrd.open_workbook(local_file) sheet = book.sheet_by_index(0) for row in range(1, sheet.nrows): row_date = str(sheet.cell_value(row, 0)) if row_date == date_str: return sheet.cell_value(row, 8) except Exception as e: print(f"下载股息率失败: {e}") return None ``` ### Technical Analysis The downloaded spreadsheet is written to the fixed path `/tmp/H30269indicator.xls`. Shared temporary directories are generally writable by other local users. The code does not create the file securely, verify that it is a regular file, check for symbolic links, enforce ownership, or remove it after use. A local attacker can pre-create the path as a symbolic link, potentially causing `curl` to overwrite another file that the Skill user can write. An attacker can also replace the file between download and parsing, causing the application to process attacker-controlled spreadsheet content. The constant filename additionally creates interference between concurrent instances of the update and monitoring scripts. ### Attack Path 1. A local attacker predicts the fixed `/tmp/H30269indicator.xls` path. 2. Before execution, the attacker creates that path as a symbolic link to a file writable by the Skill user, or repeatedly replaces the temporary file during execution. 3. The Skill invokes `curl -o` against the fixed p ...[truncated 808 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Use `tempfile.NamedTemporaryFile()` or `tempfile.TemporaryDirectory()` to obtain an unpredictable, securely created path. - Restrict temporary-file permissions to the current user. - Download with a Python HTTPS client and write through the already-secured file descriptor where possible. - Check the HTTP status, response size, and expected content type before parsing. - Close and delete the temporary file in a `finally` block. - Avoid sharing a temporary filename between the update and monitoring processes. - If a persistent cache is required, place it in a dedicated private cache directory and use locking plus atomic replacement. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (21)

YARA rule 'agent_skill_credential_exfiltration_webhook': AI agent skill credential harvesting followed by webhook or external exfiltration [agent_skills]

Critical
Category
YARA Match
Content
�息率溢价是否低于3%
3. 满足条件时发送Telegram通知
"""

import xlrd
import csv
import subprocess
import re
import os
from datetime import datetime, timedelta
from pathlib import Path

# 配置
DATA_DIR = "/Users/liyi/.openclaw/workspace"
CSV_FILE = os.path.join(DATA_DIR, "股息率溢价跟踪.csv")
EXCEL_FILE = os.path.join(DATA_DIR, "股息率溢价跟踪.xlsx")
TELEGRAM_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "")
TELEGRAM_CHAT_ID = "505395883"

def send_telegram(message):
    """发送Telegram消息"""
    if not TELEGRAM_TOKEN:
        print("未配置Telegram Bot Token")
        return False
    
    cmd = f"""curl -s -X POST https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage \
      -d chat_id={TELEGRAM_CHAT_ID} \
      -d text="{message}" \
      -d parse_mode=HTML"""
    
    os.system(cmd)
    return True

def download_dividend_rate(date_str):
    """下载股息率"""
    url = "https://oss-ch.csindex.com.cn/static/html/csindex/public/uploads/file/autofi
Confidence
85% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared purpose focuses on tracking and calculating a market metric, but the implementation apparently also performs outbound network access and sends Telegram notifications using a bot token, including to a hardcoded chat target. That mismatch is dangerous because hidden or under-disclosed behaviors can exfiltrate data, use secrets unexpectedly, or trigger external actions that users did not knowingly authorize; the hardcoded-recipient aspect makes the alerting path especially sensitive.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
-d text="{message}" \
      -d parse_mode=HTML"""
    
    os.system(cmd)
    return True

def download_dividend_rate(date_str):
Confidence
97% confidence
Finding
The script builds a shell command with untrusted data and executes it via os.system. The Telegram token comes from an environment variable and the message text is interpolated directly into the shell command, so shell metacharacters in either value could trigger command injection and arbitrary command execution under the script's privileges.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
url = "https://oss-ch.csindex.com.cn/static/html/csindex/public/uploads/file/autofile/indicator/H30269indicator.xls"
    local_file = "/tmp/H30269indicator.xls"
    
    os.system(f"curl -s -o {local_file} '{url}'")
    
    try:
        book = xlrd.open_workbook(local_file)
Confidence
85% confidence
Finding
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
url = "https://oss-ch.csindex.com.cn/static/html/csindex/public/uploads/file/autofile/indicator/H30269indicator.xls"
    local_file = "/tmp/H30269indicator.xls"
    
    os.system(f"curl -s -o {local_file} '{url}'")
    
    try:
        book = xlrd.open_workbook(local_file)
Confidence
91% confidence
Finding
The script invokes a shell via os.system to run curl, which is riskier than necessary because shell execution expands the attack surface and can become command injection if any interpolated value later becomes user-controlled. In this file the arguments are currently hard-coded, so this is not immediately critical, but using a shell for network retrieval in a data-tracking skill is still an unsafe pattern.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill advertises executable behavior involving shell commands, environment-variable access, and file read/write capabilities, but does not declare any explicit tool scope or permissions boundaries. This increases the risk that an agent or reviewer will underestimate the skill's operational reach, enabling unintended command execution, filesystem modification, or secret use without clear consent controls.

Session Persistence

Medium
Category
Rogue Agent
Content
### Cron Job (Daily Update)

```bash
crontab -e
# Add line:
0 17 * * * cd /path/to/skill && python3 scripts/update_dividend_premium.py --update
```
Confidence
85% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The script's natural-language interface and data retrieval are fixed to Chinese, including Chinese-only messaging and the locale parameter 'cn_ZH' in the bond yield request. There is no indication that this locale restriction is optional, user-selected, or justified as a region-specific tool.

Description-Behavior Mismatch

Medium
Confidence
90% confidence
Finding
The manifest describes the skill as tracking dividend yield, 10-year bond yield, and calculating the premium for investment decisions. This code also sends outbound Telegram messages, which is an extra operational behavior not reflected in the stated description.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This code transmits monitoring messages to Telegram using a bot token and chat ID, which is a network operation sending potentially sensitive user/system-derived data to a third party. Although the file header says notifications are sent, the code itself provides no confirmation prompt or explicit user-facing disclosure at the point of transmission beyond an internal docstring.

External Transmission

Medium
Category
Data Exfiltration
Content
print("未配置Telegram Bot Token")
        return False
    
    cmd = f"""curl -s -X POST https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage \
      -d chat_id={TELEGRAM_CHAT_ID} \
      -d text="{message}" \
      -d parse_mode=HTML"""
Confidence
86% confidence
Finding
The hardcoded Telegram API endpoint confirms third-party data transmission. In this skill's context, that transmission is intentional for alerts, but any external egress increases privacy and governance risk, especially in agent environments where users may not expect network exfiltration.

External Transmission

Medium
Category
Data Exfiltration
Content
print("未配置Telegram Bot Token")
        return False
    
    cmd = f"""curl -s -X POST https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage \
      -d chat_id={TELEGRAM_CHAT_ID} \
      -d text="{message}" \
      -d parse_mode=HTML"""
Confidence
86% confidence
Finding
The hardcoded Telegram API endpoint confirms third-party data transmission. In this skill's context, that transmission is intentional for alerts, but any external egress increases privacy and governance risk, especially in agent environments where users may not expect network exfiltration.

Tainted flow: 'cmd' from os.getenv (line 31, credential/environment) → os.system (code execution)

Medium
Category
Data Flow
Content
-d text="{message}" \
      -d parse_mode=HTML"""
    
    os.system(cmd)
    return True

def download_dividend_rate(date_str):
Confidence
98% confidence
Finding
A credential-derived environment value flows into a shell command that is executed. If the TELEGRAM_BOT_TOKEN environment variable is attacker-controlled or malformed, it can break command structure and execute arbitrary commands; combining credential handling with shell construction amplifies the risk.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The module-level documentation says to check whether dividend premium is below 3%, and the run_check comment at L131 also says '检查溢价低于3%'. However, check_premium_low actually triggers only when the premium is below 1.0, creating a direct contradiction between documentation/comments and code behavior.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The skill executes external commands to fetch remote content even though its stated purpose is simple data collection and calculation. That capability is broader than necessary and increases risk through dependency on shell tools, PATH resolution, and less controlled handling of remote data, especially in an automation context that will run unattended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def get_bond_yield(date_str):
    """从财政部官网获取10年期国债收益率"""
    result = subprocess.run(
        ['curl', '-s', 'https://yield.chinabond.com.cn/cbweb-czb-web/czb/moreInfo?locale=cn_ZH&nameType=1'],
        capture_output=True, text=True
    )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def get_bond_yield(date_str):
    """从财政部官网获取10年期国债收益率"""
    result = subprocess.run(
        ['curl', '-s', 'https://yield.chinabond.com.cn/cbweb-czb-web/czb/moreInfo?locale=cn_ZH&nameType=1'],
        capture_output=True, text=True
    )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script writes updated data to the configured CSV file and regenerates the Excel workbook, which can overwrite existing local files. Although there are status prints, there is no explicit warning or confirmation that running the script will modify files under the user's workspace.

Missing User Warnings

Low
Confidence
82% confidence
Finding
The update flow fetches data from external URLs and overwrites the local CSV file at a fixed path, which are network and file-write operations. While these actions are part of the script's purpose, there is no explicit warning in the code comments or user-facing messaging that running the default path performs both remote fetches and local data modification.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
The request URL includes `locale=cn_ZH`, which forces a specific locale in the fetched content. This is a natural-language/locale policy concern because the script does not expose a user option or explain that it is intentionally restricted to Chinese-language data.

Description-Behavior Mismatch

Low
Confidence
86% confidence
Finding
The stated purpose focuses on monitoring dividend yield, 10-year bond yield, and calculating the premium for investment decisions. This script goes beyond that by producing a formatted Excel artifact with visualization, which is an additional reporting/output feature not reflected in the description.

Static analysis

No suspicious patterns detected.