T09 · Insecure Skill Coding Practices
- Location
scripts/todo.py:213- Finding
OS Command Injection Through Unvalidated Reminder Job IDs
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This todo skill mostly matches its stated purpose, but it has a confirmed command-injection flaw in reminder cleanup and sends/stores chat routing data for reminders and status messages.
Review before installing. This skill can store todos, copy attachments, remember chat routing, send task lists/status to a configured chat target, and create scheduled reminders. It should be fixed to remove all shell=True reminder deletion calls, validate cron job IDs, harden attachment path handling, and require clearer confirmation for external sends and destructive actions.
scripts/todo.py:213OS Command Injection Through Unvalidated Reminder Job IDs
scripts/todo.py:361Attachment Path Restrictions Can Be Bypassed With Symbolic Links
scripts/merge-reminders.py:34Reminder Migration Stores Job Names Instead of Actual Job IDs and Does Not Remove Existing Jobs
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
- **状态**: ✅ 已发布
- **Changelog**: `安全修复:移除shell=True,添加依赖声明,增强文件访问安全`
- **修复内容**:
1. 移除 `subprocess.run(shell=True)` 使用,改用安全的 args 列表形式
2. 在 metadata 中声明 Python 3 和 OpenClaw CLI 依赖
3. 添加文件访问安全措施(禁止访问系统目录、文件大小限制、权限检查)
4. 添加详细的安全说明文档
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
问题: create_cron_job() 使用 subprocess.run(cmd, shell=True)
修复: 改用 args 列表形式
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
问题: create_cron_job() 使用 subprocess.run(cmd, shell=True)
修复: 改用 args 列表形式
The skill documents commands that actively send status and list data to external chat targets and store channel/target identifiers for later reuse, but this behavior is not clearly reflected in the top-level description. That mismatch can cause users or calling agents to treat the skill as local-only task management when it actually performs outbound communication and session persistence.
The skill documents commands that actively send status and list data to external chat targets and store channel/target identifiers for later reuse, but this behavior is not clearly reflected in the top-level description. That mismatch can cause users or calling agents to treat the skill as local-only task management when it actually performs outbound communication and session persistence.
The attachment feature explicitly supports copying, storing, and deleting user-specified local files, which exceeds the minimum capability needed for a todo manager and creates a path for unintended file access or destructive file operations. In the surrounding context, the skill also surfaces attachment paths in outputs, increasing the chance of sensitive local data exposure and harmful file handling.
This is a true tool-parameter abuse issue because untrusted old_job_id data is concatenated into a shell command. The todo skill stores and later reuses external identifiers, so compromised reminder state can turn routine reminder maintenance into arbitrary command execution.
if reminder_name in reminders:
for old_job_id in reminders[reminder_name].get("job_ids", []):
try:
subprocess.run(f"openclaw cron delete {old_job_id}", shell=True, capture_output=True)
except:
pass
This path abuses a tool parameter by passing job_id through a shell-interpreted command string. Because the parameter comes from stored reminder metadata, an attacker can potentially persist a payload that executes later when a bucket is deleted.
# 删除所有关联的 cron 任务
for job_id in reminders[reminder_name].get("job_ids", []):
try:
subprocess.run(f"openclaw cron delete {job_id}", shell=True, capture_output=True)
except:
pass
The update-due workflow contains the same shell-based tool-parameter abuse, allowing malicious job IDs to escape into shell syntax. This is particularly dangerous because ordinary user actions like changing deadlines may trigger the vulnerable path.
# 先删除旧的 cron
for job_id in old_reminder.get("job_ids", []):
try:
subprocess.run(f"openclaw cron delete {job_id}", shell=True, capture_output=True)
except:
pass
old_reminder["job_ids"] = []
This is another confirmed command-injection sink via tool parameter abuse: a stored job_id is inserted into a shell command during reminder bucket updates. In the context of a personal productivity skill, such hidden execution capability is more dangerous because users will not expect a todo action to execute arbitrary shell fragments.
# 删除旧的 cron 任务
for job_id in new_reminder.get("job_ids", []):
try:
subprocess.run(f"openclaw cron delete {job_id}", shell=True, capture_output=True)
except:
pass
This markdown file contains the skill's checklist and operational documentation exclusively in Chinese, including headings, instructions, and safety notes. Under the policy, forcing a specific language without user opt-in or a documented justification can be a natural-language policy violation.
The skill description is written entirely in Chinese and does not indicate that language selection is optional or that the skill is intended only for a Chinese-speaking or region-specific audience. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.
The skill invokes Python scripts that read and write files and use shell-accessible external tooling, yet the manifest declares no explicit tool scope or permissions boundary. This creates an authorization ambiguity where an agent may run capabilities broader than a user would reasonably expect, especially given attachment handling, persistent storage, and outbound messaging via OpenClaw.
The trigger conditions include very broad everyday terms such as '待办', 'Todo', and direct variants that are likely to collide with ordinary conversation. Over-broad activation increases the chance the skill runs unexpectedly, which is more dangerous here because it can write persistent state and send messages to saved chat targets.
The direct-send section instructs the agent to send status or task lists to the current chat target and to persist channel/target details, but it does not present a prominent user-facing warning that task data will be transmitted externally. Users may assume the output is local to the assistant response rather than pushed through an external messaging path, creating confidentiality and consent risks.
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.
# 创建项目
python3 todo.py project create "标签" "项目名称" [-d "描述"]
# 列出项目
python3 todo.py project list
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.
# 创建项目
python3 todo.py project create "标签" "项目名称" [-d "描述"]
# 列出项目
python3 todo.py project list
The trigger description is very broad and can match common conversation about tasks, making accidental activation likely. In skill systems, overbroad triggering is dangerous because it can cause the agent to enter a stateful tool flow and read or modify persisted user data without sufficiently clear intent.
Stating that reminders include attachment paths exposes local filesystem locations without any privacy warning or minimization. File paths often reveal usernames, project names, directory structure, or sensitive context even when file contents are not shown.
The skill specifies that reminders include attachment paths and that display behavior includes task details in plain-language outputs. This can leak sensitive local filesystem information and user-provided content into conversational channels where it may be exposed more broadly than intended.
The invocation rule uses a catch-all for any request containing very common words like '待办', '任务', or 'todo', which can over-trigger in unrelated contexts. Because this skill can delete tasks, attach files, and manage reminders, ambiguous activation materially raises the risk of unintended data access or modification.
该技能宣称的用途是待办事项管理,但参考文档把核心使用方式定义为直接运行本地 Python 脚本并切换目录执行。这意味着技能依赖通用本地代码执行能力,而不是受限的待办接口;对一个待办管理技能而言,这种能力并非由其业务目的本身正当化。
The instructions explicitly say to reveal attachment information during task viewing/completion and to proactively mention or offer attachments. Proactive disclosure is risky because it can surface sensitive filenames and locations even when the user did not ask for them.
The documentation says deleting a task also deletes all attachment files, but it does not provide a prominent user warning that this is destructive and may be irreversible. Given the attachment model accepts local files, users may not understand the consequences and could lose data unexpectedly.
The task detail example includes a full local attachment path, and the documented reminder behavior surfaces attachment location context. This unnecessarily exposes filesystem layout and potentially sensitive identifiers embedded in paths, increasing privacy and reconnaissance risk.
No suspicious patterns detected.