Security checks for vulnerabilities and agentic risk
Overview
This is mostly a normal expense logging skill, but it can set up recurring monthly report delivery and back up private financial reports to a fixed folder without a clear opt-in step.
Review this before installing if your spending data is sensitive. Require explicit confirmation before creating, editing, deleting, exporting, backing up, or scheduling reports; disable or inspect any openclaw cron task; and change the backup location if /volume2/Download/OpenClaw/reports/ is shared or synchronized.
The data-loading validation only confirms that required keys exist:
python
for i, rec in enumerate(data):
if not isinstance(rec, dict):
raise ValueError(f"Record {i+1} is not an object: {rec!r}")
for field in REQUIRED_FIELDS:
if field not in rec:
raise ValueError(
f"Record {i+1} is missing field '{field}': {rec!r}"
)
Technical Analysis
The category value is inserted directly into the quoted data-cat HTML attribute. It is not escaped with html.escape(value, quote=True) at this output location.
Although records normally created by crud.py restrict categories to the configured allowlist, report.py accepts an arbitrary expenses file through --file. The shared load_data() function only checks the container type and presence of required fields. It does not enforce the category allowlist, validate field types, or reject attribute-breaking characters.
Consequently, a crafted JSON record can close the data-cat attribute and introduce an event-handler attribute. Because the generated report includes executable JavaScript and is intended to be opened in a browser, this becomes stored client-side script execution.
Attack Path
An attacker creates or modifies an expenses JSON file containing all required keys.
The attacker assigns the category field a value such as:
text
" onmouseover="alert(document.domain)
The victim or Agent invokes:
bash
...[truncated 940 chars]
Remediation
View remediation
Remediation Suggestions
Escape every dynamic value used in HTML attributes:
python
safe_cat = html_mod.escape(str(cat), quote=True)
Use safe_cat in data-cat and all other attribute contexts.
Enforce a strict schema in load_data():
Require category to be a member of CATEGORIES.
Require description, date, and id to be strings.
Validate dates using the expected format.
Require amounts to be finite, positive numeric values.
Require timestamps to be valid integers.
Keep output encoding context-specific. Use HTML text escaping for text nodes and quote-aware escaping for attribute values.
Add regression tests using quotes, angle brackets, event-handler fragments, and malformed record types.
Consider applying a restrictive Content Security Policy to generated reports, ideally moving inline JavaScript to a separate trusted file or using a nonce/hash-based policy.
T09 · Insecure Skill Coding Practices
Warning
Location
scripts/report.py:1232
Finding
Spreadsheet Formula Injection in CSV Exports
Content
View full analysis
Vulnerability Details
File Location: scripts/report.py:1232-1237 Vulnerability Type: CSV formula injection Risk Level: Medium
Vulnerable Code
python
with open(output_file, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerow(['Date', 'Category', 'Description', 'Amount'])
for e in month_expenses:
writer.writerow([
e['date'],
e['category'],
e.get('description', ''),
f"{e['amount']:.2f}"
])
Technical Analysis
csv.writer correctly handles CSV quoting and delimiters, but CSV quoting does not prevent spreadsheet applications from interpreting cell contents as formulas.
The expense description is user-controlled and is written without neutralizing values beginning with formula-triggering characters such as =, +, -, or @. Some spreadsheet clients also ignore leading tabs or carriage returns when deciding whether to evaluate a cell.
Therefore, an expense description can become an executable spreadsheet formula when the exported file is opened in a compatible desktop spreadsheet application.
Attack Path
An attacker causes an expense record to be created with a description beginning with a spreadsheet formula.
For example, the description may contain a formula that references an attacker-controlled external resource.
The victim generates a CSV report:
bash
python3 scripts/report.py --csv --month 2026-04
The raw description is written into the CSV cell.
The victim opens the exported file in a spreadsheet application that evaluates formulas.
The spreadsheet evaluates the attacker's content, subject to the application's security settings and prompts.
Impact Assessment
Depending on the spreadsheet client, security configuration, and user interaction, exploitation may cause outbound requests, leak spreadsheet data, cr
...[truncated 291 chars]
Remediation
View remediation
Remediation Suggestions
Sanitize every string cell before passing it to csv.writer.
Detect formula-leading characters after removing or accounting for leading whitespace, tabs, carriage returns, and line feeds.
Prefix potentially dangerous cells with an apostrophe or another spreadsheet-safe neutralization character:
python
def safe_csv_cell(value):
value = str(value)
checked = value.lstrip(' \t\r\n')
if checked.startswith(('=', '+', '-', '@')):
return "'" + value
return value
Apply this function to descriptions, categories, dates, labels, and any future user-controlled string fields.
Document that CSV exports are intended as data rather than trusted spreadsheet programs.
Add tests covering formula prefixes, leading whitespace, tabs, embedded line breaks, Unicode text, and ordinary negative numeric fields.
T05 · Unauthorized Access and Privilege Escalation
Note
Location
SKILL.md:112
Finding
Automatic Scheduled Execution and External Storage of Financial Reports Without Explicit Opt-In
The instruction at line 112 requires generated reports to be sent to the user and additionally backed up to the following fixed external directory:
text
/volume2/Download/OpenClaw/reports/
The instruction at line 130 directs the Agent to configure an OpenClaw cron task after expense tracking is first established:
text
Configure an OpenClaw cron task to generate and send the previous month's
report at 08:00 on the first day of every month. Trigger this configuration
after expense tracking is first set up.
Technical Analysis
Financial reports contain sensitive personal information. The Skill directs the Agent to copy these reports outside the Skill's private data directory into a fixed download location, which may be shared, synchronized, broadly readable, or retained under different access controls.
The Skill also directs the Agent to create a recurring scheduled task automatically after initial setup. A cron task survives the original interaction and causes future execution and report transmission. The instructions do not require a separate, explicit user confirmation before either side effect.
The Python scripts reviewed do not themselves perform the external backup or install the cron task. The risk arises if an Agent follows the instructions in SKILL.md.
Attack Path
A user performs initial expense-tracking setup.
The Agent follows the Skill instruction and creates a recurring OpenClaw cron task without a dedicated consent prompt.
On the first day of subsequent months, the task generates and sends the previous month's report.
The Agent also copies reports to the fixed external download directory.
Other users, services, synchronization tools, or processes with access
...[truncated 681 chars]
Remediation
View remediation
Remediation Suggestions
Require a clear, separate user confirmation before creating any scheduled task.
Show the exact schedule, command, report recipient, and data involved before obtaining consent.
Do not copy reports to a fixed external location by default. Store them in a private Skill-controlled directory with restrictive permissions.
If backup is requested, allow the user to select the destination and confirm whether it is shared or synchronized.
Provide commands to list, disable, and delete the installed cron task.
Define report retention and deletion behavior.
Verify the destination and recipient before every automated transmission, or require periodic renewal of consent.
Use restrictive file permissions and avoid exposing financial reports in globally readable download directories.
声明描述的是一个完整的个人记账技能,重点包括“自然语言记账”和“自动分类”。但提供的代码没有任何用户输入解析、记账写入、消费录入或分类判定逻辑;它只调用 load_data 读取现有数据,再根据月份生成静态 HTML 报表,或导出 CSV。代码确实覆盖了声明中的一部分能力,如月度统计、预算提醒(通过预算占比/超支提示)和月度报表生成,但缺失了被明确宣称的核心能力:记账本身和自动分类。此外,代码还包含未声明的 CSV 导出以及为多个历史月份批量生成报表的行为。因此,描述不能准确代表该代码块的实际功能,存在明显不匹配。
Content
No source excerpt is available for this finding.
Vague Triggers
Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding
The README advertises very broad natural-language triggers such as short everyday phrases like “午饭35” and “昨天打车花了25”, with no clear confirmation step, invocation boundary, or exclusions. In an agent context, this increases the chance of accidental activation and unintended data writes when a user is merely chatting or providing context rather than explicitly intending to invoke the skill.
Content
No source excerpt is available for this finding.
Missing User Warnings
Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding
The skill documentation exposes state-changing capabilities including deleting recent records, modifying entries, and writing local expense/config data, but does not warn users about these side effects or describe safeguards. In a financial logging skill, unintended edits or deletions can corrupt personal records and make users unaware that private spending data is being persisted locally.
Content
No source excerpt is available for this finding.
Undeclared Tool Scope
Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill instructs the agent to read and write local files (data/expenses.json, data/config.json) but does not declare any explicit tool scope or permissions boundary. This weakens least-privilege controls and can allow broader-than-expected filesystem access if the runtime grants default file capabilities.
Content
No source excerpt is available for this finding.
Vague Triggers
Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding
The trigger phrases are broad enough to activate on common finance-related conversation, which can cause the skill to ingest, persist, or act on sensitive spending information when the user did not intend to invoke it. In a personal-finance context this increases privacy risk because ordinary chat may be treated as a command to store or process personal data.
Content
No source excerpt is available for this finding.
Missing User Warnings
Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding
The skill states that generated HTML reports will be sent to the user and backed up to /volume2/Download/OpenClaw/reports/ without an explicit privacy notice, retention policy, or opt-in. Spending reports contain highly sensitive personal financial data, so silent export and backup increase the chance of unintended disclosure through shared storage, device compromise, or overbroad file access.
Content
No source excerpt is available for this finding.
Missing User Warnings
Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding
Automatic monthly report generation and sending via cron creates recurring disclosure of personal finance data without an explicit, informed user consent flow. Because this is scheduled and persistent, the risk is amplified: sensitive reports may continue to be generated and transmitted long after the user forgets the setting or no longer expects the behavior.
Content
No source excerpt is available for this finding.
Natural-Language Policy Violations
Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding
The file’s user-facing natural language is consistently Chinese in the module docstring, CLI description, help text, and error/output messages. This imposes a specific language/locale on users without any opt-in or documented regional justification, which matches the language-policy violation criterion.
Content
No source excerpt is available for this finding.
Natural-Language Policy Violations
Low
Category
Not specified by scanner
Confidence
95% confidence
Finding
Natural-language policy violations include forcing a specific language without user opt-in. This file presents all user-facing instructions and trigger phrases only in Chinese, with no indication that other languages are supported or that the locale constraint is intentional and justified.
Content
No source excerpt is available for this finding.
Natural-Language Policy Violations
Low
Category
Not specified by scanner
Confidence
72% confidence
Finding
The manifest and examples are entirely in Chinese and define Chinese trigger phrases, with no indication that the user can choose another language or locale. This can constitute a language-policy issue when a skill implicitly enforces one language without opt-in or documented justification.
Content
No source excerpt is available for this finding.
Natural-Language Policy Violations
Low
Category
Not specified by scanner
Confidence
95% confidence
Finding
This code includes user-facing docstrings, help text, warnings, and printed output entirely in Chinese, with no option for users to select another language. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is explicitly justified.
Content
No source excerpt is available for this finding.
Natural-Language Policy Violations
Low
Category
Not specified by scanner
Confidence
95% confidence
Finding
The file description explicitly states the report is generated in Chinese, and the HTML later fixes the page language to Chinese as well. This enforces a specific language/locale choice without any user opt-in or explanation that the tool is region-specific.
Content
No source excerpt is available for this finding.
Natural-Language Policy Violations
Low
Category
Not specified by scanner
Confidence
97% confidence
Finding
The generated document declares a fixed zh-CN locale, which is a natural-language policy concern when no user selection mechanism is provided. Nothing in this file indicates the locale is optional or justified as a region-specific requirement.