Back to skill

Security audit

My skill for SmartSchedule

Security checks for vulnerabilities and agentic risk

Overview

This is a real shared schedule tool, but it sets up recurring background agents and external reminders in ways that need review before use.

Install only if you are comfortable with a shared team calendar whose entries can be emailed and sent through DingTalk by recurring background jobs. Before production use, restrict who can add/update/delete schedules, store SMTP credentials outside the tracked project config, escape schedule fields in HTML email, and ensure cron reminder agents cannot treat schedule text as instructions.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (3)

T01 · Skill Instruction Hijacking

Warning
Location
scripts/check_upcoming.py:24
Finding
Stored Indirect Prompt Injection Through Schedule Reminder Content<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:159-159`, `SKILL.md:182-182`; `scripts/check_upcoming.py:24-39` **Vulnerability Type**: Stored indirect prompt injection across an autonomous agent boundary **Risk Level**: Medium ### Vulnerable Code The reminder formatter incorporates user-controlled schedule fields into a message: ```python def format_reminder(schedules): """Format upcoming schedules as a reminder message.""" if not schedules: return None lines = ["⏰ Schedule reminder! The following schedules are about to begin:", ""] for s in schedules: start = datetime.strptime(s["start_time"], DATE_FMT) now = datetime.now() minutes_left = int((start - now).total_seconds() / 60) loc = f" | 📍 Location: {s['location']}" if s.get("location") else "" lines.append(f"📅 {s['title']}") lines.append(f" ⏰ {s['start_time']} (starts in approximately {minutes_left} minutes){loc}") if s.get("description"): lines.append(f" 📝 {s['description']}") lines.append("") ``` The autonomous cron instructions require an agent to read the generated JSON and forward its `message` field verbatim: ```json { "message": "AUTONOMOUS: Execute the team schedule reminder check. Steps: 1) Execute python3 schedule-manager/scripts/check_upcoming.py 2) Read the output JSON 3) If status is reminders_sent, send the contents of the message field verbatim to the user as a reminder. If status is no_upcoming, take no action. Do not reply HEARTBEAT_OK." } ``` ### Technical Analysis Schedule titles, descriptions, and locations originate from users of the shared calendar. These values are stored in SQLite and later included without a trust-boundary marker in output consumed by an autonomous language-model session. The cron workflow asks the agent to interpret the script output and act on it. Consequently, a malicious schedule value can contain text that resembles agent instruct ...[truncated 1895 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the language model from the reminder delivery path. Have a deterministic component parse the script's structured JSON and send the rendered message directly through a narrowly scoped messaging API. 2. Preserve schedule data as separate structured fields rather than combining it with autonomous instructions. 3. If an agent must remain involved, define schedule fields explicitly as untrusted data that must never be treated as instructions. 4. Restrict the cron session to the minimum required tool set, ideally allowing only execution of the reminder script and delivery to a preconfigured destination. 5. Enforce an output schema and reject any attempted destination, tool, command, or task change originating from schedule fields. 6. Consider validating or limiting calendar text length and control characters. Content filtering can provide defense in depth but must not be the primary security boundary. 7. Add adversarial tests using schedule values that contain instruction-like text and verify that no additional tool calls occur. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/email_summary.py:69
Finding
Unescaped User-Controlled Fields in HTML Email Summaries<![CDATA[ ## Vulnerability Details **File Location**: `scripts/email_summary.py:69-79` **Vulnerability Type**: HTML injection in generated email **Risk Level**: Medium ### Vulnerable Code ```python else: html += f'<p style="color: #555;">Total <strong>{len(schedules)}</strong> schedules:</p>' for i, s in enumerate(schedules, 1): loc_html = f'<br>📍 {s["location"]}' if s.get("location") else "" desc_html = f'<br><em style="color:#888">{s["description"]}</em>' if s.get("description") else "" html += f""" <div style="background:#f8f9fa; border-left:4px solid #3498db; padding:12px; margin:10px 0; border-radius:4px;"> <strong style="font-size:15px;">{i}. {s['title']}</strong><br> ⏰ {s['start_time']} ~ {s['end_time']} {loc_html}{desc_html} </div> """ ``` ### Technical Analysis The schedule title, location, and description are inserted directly into an HTML document without HTML escaping. These values are controlled by users who can add or update records in the shared calendar. An attacker can therefore store HTML elements or attributes in a schedule field. When `build_email()` generates the hourly summary, the markup becomes part of the email's HTML MIME body. Although many mail clients block scripts, they commonly permit some links, images, and formatting. The vulnerability can consequently be used for deceptive content, phishing links, layout manipulation, or externally hosted tracking resources. The plain-text MIME alternative does not mitigate the vulnerable HTML alternative when a recipient's mail client renders HTML. ### Attack Path 1. An attacker with shared-calendar access creates or updates a schedule. 2. The attacker supplies HTML markup in the title, description, or location, such as a deceptive link or an externally hosted image. 3. The value is persisted in SQLite without being transformed. 4. The hourly cron task executes `scripts/email_summary.py`. 5. `bu ...[truncated 938 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape every database-derived value before placing it into HTML: ```python from html import escape title = escape(str(s.get("title", "")), quote=True) location = escape(str(s.get("location", "")), quote=True) description = escape(str(s.get("description", "")), quote=True) start_time = escape(str(s.get("start_time", "")), quote=True) end_time = escape(str(s.get("end_time", "")), quote=True) ``` 2. Use only the escaped variables when constructing the HTML MIME body. 3. Prefer a template engine configured with automatic HTML escaping rather than constructing markup with f-strings. 4. Do not attempt to preserve user-provided HTML unless a mature allowlist-based sanitizer is applied. 5. Consider prohibiting remote images and rewriting or validating links if calendar content should not contain external resources. 6. Add tests covering tags, attributes, entity encoding, quotes, malformed markup, and externally loaded resources. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/config.json:2
Finding
SMTP Authorization Credential Designed to Be Stored in a Plaintext Project Configuration File<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config.json:2-9`; `scripts/email_summary.py:21-23`, `scripts/email_summary.py:96-107` **Vulnerability Type**: Plaintext credential storage **Risk Level**: Low ### Vulnerable Code The project configuration contains a plaintext password field: ```json { "smtp": { "server": "smtp.qq.com", "port": 465, "use_ssl": true, "sender_email": "your-sender-email@example.com", "password": "your-smtp-password", "receiver_email": "your-receiver-email@example.com" } } ``` The complete file is loaded directly from the project directory: ```python def load_config(): with open(CONFIG_PATH, "r", encoding="utf-8") as f: return json.load(f) ``` The configured value is then used for SMTP authentication: ```python def send_email(msg, config): smtp_cfg = config["smtp"] if smtp_cfg.get("use_ssl"): server = smtplib.SMTP_SSL(smtp_cfg["server"], smtp_cfg["port"]) else: server = smtplib.SMTP(smtp_cfg["server"], smtp_cfg["port"]) server.starttls() server.login(smtp_cfg["sender_email"], smtp_cfg["password"]) server.send_message(msg) server.quit() ``` ### Technical Analysis The committed file currently contains placeholders rather than a live credential. However, the documented deployment process instructs administrators to replace the placeholder with a real SMTP authorization code in this project file. This design places the production credential in plaintext alongside source files. No `.gitignore` file or runtime permission enforcement was present in the audited project. As a result, the credential may be exposed through source-control commits, project archives, backups, support bundles, or permissive local filesystem access. The issue is credential-at-rest exposure rather than network plaintext transmission. The default configuration uses `SMTP_SSL`, and the non-SSL branch invokes STARTTLS before authentication. ### Attack Path 1 ...[truncated 1201 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `password` field from the tracked runtime configuration. 2. Read the credential from a protected environment variable or a dedicated secret manager: ```python password = os.environ["SCHEDULE_SMTP_PASSWORD"] ``` 3. Keep only a redacted `config.example.json` in source control. 4. Add the live configuration and other runtime artifacts to `.gitignore`, including: - `scripts/config.json` - `scripts/schedules.db` - `scripts/schedules.db-wal` - `scripts/schedules.db-shm` 5. Create the live secret file outside the project directory if a file-based secret is unavoidable. 6. Enforce owner-only permissions such as `0600` on any credential-bearing file and restrict ownership to the service account. 7. Ensure logs and exception messages never include the credential. 8. Rotate the SMTP authorization code immediately if a populated configuration has ever been committed, archived, or shared. 9. Add secret-scanning checks to source-control and release workflows. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (26)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
代码整体仍属于日程管理/提醒相关领域,并使用了本地 schedule_manager 模块,因此与声明的核心场景部分一致。但存在两处明显不符:第一,声明提到提醒渠道是“钉钉提醒”,而代码注释和输出用途明确是由 cron 调用并通过 QQ Bot 通道推送,这是未声明且与已声明渠道不一致的能力。第二,声明特别说明“所有日程操作通过 schedule_manager.py 完成”,但该代码块是独立脚本 check_upcoming.py,负责定时查询、格式化提醒、标记已提醒并输出结果,说明实际还存在 schedule_manager.py 之外的日程操作流程。故应判定为描述与行为存在实质性不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
描述与代码的核心目的大体一致:它确实是一个本地日程管理模块,使用 Python 和 SQLite 管理日程,支持增删改查与冲突检测。代码还实现了替代时间建议、未来日程查询、即将提醒查询和提醒状态标记,均属于日程系统合理范围。但描述中明确声称支持“邮件汇总、钉钉提醒”,而提供的代码片段完全没有邮件相关实现,也没有任何钉钉集成或外部通知发送能力,只是查询哪些日程即将到期以及标记 remind_sent 状态。因此描述对能力有实质性夸大,属于描述与实际行为不完全一致,应判定为 mismatch。

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README explicitly describes a team-shared calendar plus hourly email summaries and DingTalk reminders, but it does not disclose the privacy and access-control implications to users. In this context, members may assume personal scheduling data is private when in fact all participants can view or alter shared entries and sensitive meeting details may be redistributed through email and chat notifications.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The deletion examples instruct users to cancel or delete schedules without warning that the system uses a single shared database for the whole team. In a shared-calendar skill, this creates a real integrity risk because a user may unintentionally remove events relied on by others, causing disruption or loss of important scheduling data.

Lp3

Medium
Category
MCP Least Privilege
Confidence
82% confidence
Finding
The skill instructs the agent to read local configuration and operate local scripts/SQLite data, but it declares no explicit tool scope or allowed-tools boundary. In practice this increases the chance that the agent can access broader local files than intended, especially because the skill also references cron state under ~/.openclaw/cron/jobs.json and multiple script paths.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger terms are broad and overlap with common conversation topics like schedules, reminders, meetings, and trips. In this skill, accidental invocation is more dangerous because the skill can modify shared persistent team data and register autonomous cron jobs, so a casual message could lead to unintended reads, writes, or reminder setup.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill manages a team-shared SQLite schedule and supports create/update/delete operations plus persistent reminders, but it does not clearly warn users that actions affect shared data visible to others. In group/team contexts this can cause unauthorized or accidental modification of other users' schedules, privacy leakage, and surprise persistence across sessions.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The technical document expands the skill from a local schedule manager into a multi-module system with background tasks and external integrations. That scope mismatch matters because operators and users may grant trust based on the manifest's 'local' framing while the implementation is documented to perform broader actions, increasing the chance of unnoticed data handling and unintended capability exposure.

Skill Enumeration

Medium
Category
Agent Snooping
Content
Agent 在启动时会扫描所有 Skill,当用户的请求匹配某个 Skill 时,Agent 会读取该 SKILL.md 并按照其中的指引操作。

本系统的 Skill 文件是 `skills/schedule-manager/SKILL.md`,其中定义了:
- 可用的 Python 命令及参数格式
- 添加/修改/删除/查询日程的操作步骤
- 模糊指令的解析策略
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
Agent 在启动时会扫描所有 Skill,当用户的请求匹配某个 Skill 时,Agent 会读取该 SKILL.md 并按照其中的指引操作。

本系统的 Skill 文件是 `skills/schedule-manager/SKILL.md`,其中定义了:
- 可用的 Python 命令及参数格式
- 添加/修改/删除/查询日程的操作步骤
- 模糊指令的解析策略
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
Direct SMTP use with stored credentials introduces an external exfiltration channel for schedule contents, which is broader than the stated local-management purpose. In this skill context, schedules may contain sensitive meeting names, times, locations, and notes, so automatic email delivery materially increases confidentiality risk if enabled without strict disclosure and controls.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The document states that Python scripts only perform data operations and do not handle messaging, yet elsewhere describes modules that send email over SMTP. This inconsistency can mislead reviewers about the trust boundary and conceal data egress paths, making security review and informed consent weaker.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The document describes automatic email summaries and proactive DingTalk reminders that transmit schedule data to external channels, but does not clearly state that user data will be sent out on a timer. This is dangerous because users may reasonably expect a local scheduling tool, while the documented behavior creates silent recurring disclosure of potentially sensitive calendar information.

Session Persistence

Medium
Category
Rogue Agent
Content
### 5.2 表结构

```sql
CREATE TABLE schedules (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,  -- 自增主键
    title       TEXT NOT NULL,                       -- 日程标题
    description TEXT DEFAULT '',                     -- 日程描述
Confidence
60% 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.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
清单将该技能描述为本地日程管理,且提醒能力明确写为“邮件汇总、钉钉提醒”。但该脚本模块文档说明它由定时任务调用并通过 QQ Bot 通道推送提醒,表明实际行为包含另一种未声明的提醒渠道。这个差异属于技能描述与实际实现能力不一致。

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
模块注释明确表示脚本“通过 QQ Bot 通道推送”,给人的意图是该脚本自身执行消息发送操作。实际代码只查询数据库、标记 reminded,并将结果以 JSON 打印到标准输出,没有任何 QQ API、机器人 SDK 或消息发送逻辑,因此文档与代码行为直接背离。

Missing User Warnings

Medium
Confidence
85% confidence
Finding
The script emits full schedule reminder content, including titles, times, locations, and descriptions, to stdout in JSON. In a cron/bot pipeline, stdout is often captured by logs, supervisors, or downstream integrations, which can expose sensitive calendar data to unintended readers; the local team-scheduling context makes this more concerning because meeting metadata may contain confidential business information.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code sends upcoming schedule details by email via SMTP, which transmits potentially sensitive personal calendar data to an external mail server. While the module docstring mentions hourly sending, the operational code provides no confirmation prompt or runtime user-facing disclosure before transmitting the data.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This Python file contains natural-language documentation and CLI output exclusively in Chinese, including the module docstring and later user-facing error/help text. Because the skill does not offer an opt-in or alternative locale, it enforces a specific language policy on all users.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The delete_schedule function permanently removes schedule records from the SQLite database, but there is no confirmation prompt, visible user-facing warning, or explanatory comment indicating that this action is destructive. In this file, the CLI exposes the delete command directly, so users may trigger irreversible data loss without any disclosure.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
L005 及全文均以中文直接规定技能描述和交互方式,但没有说明该技能仅面向特定中文环境用户,也未提供语言选择。按语言/区域政策,若强制特定语言而无用户选择或明确、合理的地域限定,属于自然语言策略问题。

Natural-Language Policy Violations

Low
Confidence
74% confidence
Finding
SQP-3 适用于所有文件,需检查自然语言层面的语言/地区政策问题。该文档从标题到所有用户交互示例均默认并固定为中文,未说明是否支持其他语言、是否仅面向特定中文用户群体,或是否允许用户自主选择语言,因此存在潜在的语言策略不透明问题。

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The docstrings and reminder strings are exclusively in Chinese, and the file does not state that the skill is intended only for Chinese-speaking users or offer any language selection. This can violate language/locale policy when a skill forces one language without user opt-in or documented justification.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The config sets the timezone to "Asia/Shanghai", which imposes a specific locale setting in a natural-language-adjacent configuration value. Under the policy, locale constraints should either be user-selectable or clearly justified as region-specific; neither is evident in this file.

Missing User Warnings

Low
Confidence
78% confidence
Finding
The login step uses a configured password to authenticate to the SMTP server, which is sensitive credential material. The file contains no warning, comment, or user-facing disclosure about credential use or the need to protect the configuration containing these secrets.

Static analysis

No suspicious patterns detected.