T09 · Insecure Skill Coding Practices
- Location
scripts/health-check.sh:77- Finding
Health-check output exposes Feishu application credentials
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This stock-monitoring skill is mostly purpose-aligned, but it includes risky setup and health-check behavior that can expose messaging credentials or create persistent scheduled execution without enough guardrails.
Review carefully before installing. Do not run scripts/health-check.sh with real Feishu credentials unless it is fixed to redact app_secret, avoid the sudo launchctl and setup-sleep.sh power-setting guidance, and prefer a pinned dependency install in an isolated environment. Expect the skill to create scheduled jobs and send market reports to configured Feishu or Telegram recipients.
scripts/health-check.sh:77Health-check output exposes Feishu application credentials
setup.py:18Shell-command injection through unquoted installation paths
setup.py:30Automatic installation of unpinned Python dependencies
stock_monitor.py:273Fund identifiers and valuation data are transported over plaintext HTTP
scripts/setup-sleep.sh:7Optional setup script performs unnecessary administrator-level power-policy changes
SKILL.md:90Documentation recommends unnecessary root-level LaunchAgent loading
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
join(missing)}", file=sys.stderr)
print("[ERROR] 请在 config.yaml 或 config.local.yaml 中填写飞书凭证", file=sys.stderr)
print("[ERROR] 参见 SKILL.md 的「飞书推送配置」章节", file=sys.stderr)
print(report_text) # 降级到控制台输出
return
try:
# 1. 获取 tenant_access_token
token_resp = requests.post(
'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
json={'app_id': app_id, 'app_secret': app_secret},
timeout=10
)
token_resp.raise_for_status()
access_token = token_resp.json().get('tenant_access_token', '')
if not access_token:
raise ValueError("No access_token returned")
# 2. 发送消息到 chat_id
msg_resp = requests.post(
'https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id',
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
raise ValueError("No access_token returned")
# 2. 发送消息到 chat_id
msg_resp = requests.post(
'https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id',
headers={'Authorization': f'Bearer {access_token}'},
json={
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
chat_id = tg_cfg.get('chat_id', os.environ.get('TG_CHAT_ID', ''))
if bot_token and chat_id:
try:
requests.post(
f'https://api.telegram.org/bot{bot_token}/sendMessage',
json={'chat_id': chat_id, 'text': report_text},
timeout=10
The script explicitly echoes Feishu credential-related configuration values in plain text during a routine health check. Because health-check commands are commonly run interactively or captured in logs, this creates a practical secret disclosure path that could enable unauthorized use of the Feishu integration or lateral access depending on how the credentials are scoped.
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.
echo "✅ 配置完成!"
echo ""
echo "💡 提示:"
echo "- 如果需要恢复默认设置,运行: sudo pmset -a womp 1 && sudo pmset -a powersave 1"
echo "- 查看详细设置: pmset -g assertions"
Using shell=True in a generic command runner amplifies tool-parameter abuse risk because every caller passes a fully composed shell command string. In this installer, commands include filesystem paths and shell metacharacters for pipelines/redirection, so attacker-controlled installation locations or environment manipulation could turn benign maintenance operations into arbitrary command execution.
def run(cmd, check=True, timeout=30):
"""执行 shell 命令"""
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
if check and result.returncode != 0:
print(f"[ERROR] Command failed: {cmd}")
print(f"STDERR: {result.stderr}")
The skill self-installs dependencies and proceeds to set up OS-level scheduled execution despite its primary stated purpose being stock monitoring. In agent ecosystems, self-install plus persistence is materially more dangerous because skills are expected to be task-focused and minimally invasive.
The natural-language instructions, warnings, setup steps, and usage guidance are presented exclusively in Chinese throughout the file. This can violate a language/locale policy when a skill forces a specific language without giving users an explicit choice or documenting a justified locale restriction.
Lines L005-L006 assert that private credential-containing configuration is kept out of published skill contents and that regenerated configs avoid secret material. However, the published SKILL.md then instructs users to place Feishu credentials directly into config.yaml and shows the secret-bearing fields in the example at L057-L063, undermining the stated safety guarantee.
Reference to the plist file is part of the same persistence mechanism: a LaunchAgent configuration file is a standard macOS autorun artifact. Because the skill claims first-run registration of scheduled tasks and handles private config/credentials, persistence expands the blast radius of any future compromise or misconfiguration.
解决:
launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
# 如需管理员权限,加上 sudo
sudo launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
Reference to the plist file is part of the same persistence mechanism: a LaunchAgent configuration file is a standard macOS autorun artifact. Because the skill claims first-run registration of scheduled tasks and handles private config/credentials, persistence expands the blast radius of any future compromise or misconfiguration.
解决:
launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
# 如需管理员权限,加上 sudo
sudo launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
解决:
launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
# 如需管理员权限,加上 sudo
sudo launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
解决:
launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
# 如需管理员权限,加上 sudo
sudo launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
The plist reference at this line is again part of a persistence path, but in the higher-risk context of a sudo-assisted load command. Persistence combined with elevated execution can allow recurring access to files, network destinations, and secrets without further user interaction.
launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
# 如需管理员权限,加上 sudo
sudo launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
The plist reference at this line is again part of a persistence path, but in the higher-risk context of a sudo-assisted load command. Persistence combined with elevated execution can allow recurring access to files, network destinations, and secrets without further user interaction.
launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
# 如需管理员权限,加上 sudo
sudo launchctl load ~/Library/LaunchAgents/com.openclaw.stock-monitor.plist
User-facing strings, comments, logs, and the module description are written entirely in Chinese, which effectively forces a specific language for operators and users. The file does not provide an alternative language option, opt-in mechanism, or justification that this skill is intended only for a Chinese-language environment.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def check_launchd():
"""检查 LaunchAgent 运行状态"""
try:
result = subprocess.run(
['launchctl', 'list', LAUNCHD_LABEL],
capture_output=True, text=True, timeout=10
)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
try:
# 加载服务
result = subprocess.run(
['launchctl', 'bootstrap', 'gui/$UID', plist],
capture_output=True, text=True, timeout=15
)
Unlike the other RA2 hits, this line actively bootstraps a user LaunchAgent, which modifies session persistence. In the context of a health-check script, automatically restoring persistence without explicit user confirmation can be dangerous because it changes system state and can keep the monitored skill running indefinitely after a simple check operation.
try:
# 加载服务
result = subprocess.run(
['launchctl', 'bootstrap', 'gui/$UID', plist],
capture_output=True, text=True, timeout=15
)
if result.returncode == 0:
The docstring states the generated minimal config template contains no private data or credentials, but the template written by the function includes Feishu credential fields such as app_id, app_secret, and chat_id. Even though these are placeholder values rather than real secrets, the documentation actively contradicts the code's behavior about whether credential material is present in the generated file.
The health check performs a real outbound Feishu authentication and sends a real message using configured credentials as part of a routine check, without an explicit confirmation at the point of action. In a skill context, that means merely running a 'health check' can trigger external side effects and transmit secrets to a third-party API, which is risky if the user did not expect networked actions.
This duplicate finding points to the same credential-bearing Feishu token request. The security issue is not the HTTPS call itself, but that a background health check automatically uses and transmits secrets externally without a strong consent boundary.
chat_id = feishu.get('chat_id')
# 获取 token
resp = requests.post(
'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
json={'app_id': app_id, 'app_secret': app_secret},
timeout=10
This duplicate finding points to the same credential-bearing Feishu token request. The security issue is not the HTTPS call itself, but that a background health check automatically uses and transmits secrets externally without a strong consent boundary.
chat_id = feishu.get('chat_id')
# 获取 token
resp = requests.post(
'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
json={'app_id': app_id, 'app_secret': app_secret},
timeout=10
This duplicate finding refers to the same live message send operation. The dangerous aspect in context is the unprompted external side effect and disclosure of activity timing/state to a third-party messaging platform during a nominal health check.
# 发送测试
msg = f"✅ 股票监控定时任务健康检查\n时间: {datetime.now().strftime('%H:%M:%S')}\n状态: 一切正常"
resp = requests.post(
'https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id',
headers={'Authorization': f'Bearer {token}'},
json={
This duplicate finding refers to the same live message send operation. The dangerous aspect in context is the unprompted external side effect and disclosure of activity timing/state to a third-party messaging platform during a nominal health check.
# 发送测试
msg = f"✅ 股票监控定时任务健康检查\n时间: {datetime.now().strftime('%H:%M:%S')}\n状态: 一切正常"
resp = requests.post(
'https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id',
headers={'Authorization': f'Bearer {token}'},
json={