Back to skill

Security audit

ledger cn

Security checks for vulnerabilities and agentic risk

Overview

This is a local personal ledger skill, but it needs Review because broad triggers, overstated export/sync claims, and unsafe ledger-name path handling could expose or modify financial data unexpectedly.

Install only if you are comfortable with a Chinese-localized local ledger tool that stores financial records on disk. Avoid using untrusted ledger names, do not rely on the advertised Feishu/CSV sync features until they are clearly implemented with consent controls, and prefer a version that validates ledger paths before writing files.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
src/db/connection.py:27
Finding

Ledger Name Path Traversal Enables Filesystem Writes Outside the Storage Root

Content
View full analysis

Vulnerability Details

File Location: src/db/connection.py:27-38
Vulnerability Type: Path traversal and unrestricted filesystem write
Risk Level: Medium
Exposed Inputs: src/cli.py:475-476, src/cli.py:487-488, src/cli.py:495-496, src/cli.py:503-504

Vulnerable Code

python
def get_db_path(ledger_name: str, base_path: Optional[str] = None) -> str:
    """
    Get the database file path for a ledger.
    
    Args:
        ledger_name: Name of the ledger
        base_path: Base path for ledger data (default: ~/.openclaw/skills_data/ledger/)
    
    Returns:
        Full path to the SQLite database file
    """
    if base_path is None:
        base_path = os.path.expanduser("~/.openclaw/skills_data/ledger")
    
    # If ledger name is ASCII (English), convert to lowercase
    if is_ascii(ledger_name):
        ledger_name = ledger_name.lower()
    
    ledger_dir = os.path.join(base_path, ledger_name)
    os.makedirs(ledger_dir, exist_ok=True)
    
    return os.path.join(ledger_dir, "ledger.db")

The affected value is accepted directly from CLI arguments, including:

python
create_parser.add_argument('--name', type=str, required=True, help='账本名称')
show_parser.add_argument('--name', type=str, default=None, help='账本名称')
chart_parser.add_argument('--name', type=str, nargs='+', default=None, help='账本名称(支持多个)')
add_parser.add_argument('--name', type=str, default=None, help='账本名称')

Technical Analysis

ledger_name is incorporated into a filesystem path without rejecting absolute paths, parent-directory components, path separators, or symlink-based escapes.

If ledger_name is absolute, os.path.join(base_path, ledger_name) discards base_path. A relative value containing .. can likewise escape the intended ~/.openclaw/skills_data/ledger directory. The resulting path is passed to os.makedirs() and subsequently to SQLite initialization or connection functions.

Consequently, commands that create or write led ...[truncated 1982 chars]

Remediation
View remediation

Remediation Suggestions

  1. Apply a conservative allowlist to ledger names. Permit only expected letters, digits, spaces, underscores, and hyphens, with a reasonable maximum length.

  2. Explicitly reject:

    • Absolute paths.
    • / and \ path separators.
    • . and .. path components.
    • NUL characters and control characters.
    • Empty or whitespace-only names.
  3. Canonicalize the root and candidate paths and enforce containment before creating any directory:

    python
    import re
    from pathlib import Path
    
    LEDGER_NAME_RE = re.compile(r"^[\w \-]{1,100}$", re.UNICODE)
    
    def get_db_path(ledger_name: str, base_path: Optional[str] = None) -> str:
        if not isinstance(ledger_name, str) or not LEDGER_NAME_RE.fullmatch(ledger_name):
            raise ValueError("Invalid ledger name")
    
        if Path(ledger_name).is_absolute() or ledger_name in {".", ".."}:
            raise ValueError("Invalid ledger name")
    
        root = Path(
            base_path or "~/.openclaw/skills_data/ledger"
        ).expanduser().resolve()
    
        normalized_name = ledger_name if not is_ascii(ledger_name) else ledger_name.lower()
        ledger_dir = (root / normalized_name).resolve()
    
        try:
            ledger_dir.relative_to(root)
        except ValueError:
            raise ValueError("Ledger path escapes the storage root")
    
        ledger_dir.mkdir(parents=True, exist_ok=True)
        return str(ledger_dir / "ledger.db")
    
  4. Account for symlink attacks. If the storage directory may be writable by untrusted users, reject symlink components or use filesystem operations designed to avoid following symlinks.

  5. Apply the same path-validation routine to migration helpers such as get_ledger_path() so all ledger-related paths share one trusted implementation.

  6. Add regression tests covering:

    • Absolute Unix and Windows paths.
    • ../ and nested traversal sequences.
    • Mixed path separators.
    • Symlink escapes.
    • Empty, oversized, and control-chara ...[truncated 56 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

该代码整体主用途与声明大体一致,都是个人记账/账本管理工具,并且确实实现了多账本、余额趋势、以及多账本图表等部分能力。但声明中列出的多项重要功能在此代码片段中没有对应实现或入口,尤其是自然语言记账、批量记账、期初结余初始化、分类统计、CSV导出、飞书云盘同步。虽然导入了 batch_add_transactions 和 set_opening_balance,但并未在CLI中实际暴露或调用这些能力,因此从“描述是否准确代表该代码实际行为”的角度看,描述明显超出了该代码片段实际实现范围,应判定为不匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

The supplied code is consistent with part of the declared bookkeeping purpose: it handles ledger creation, opening balance initialization, batch transaction entry, monthly summaries, and balance trends. However, the description claims a broader feature set that is not represented in this code chunk, including natural-language entry, category statistics, multi-ledger comparison, CSV export, and Feishu cloud sync. The code also behaves specifically as a hardcoded command-line demonstration script for a single local ledger rather than exposing the richer end-user functionality described. This is a partial but material description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description describes a full-featured personal accounting skill, but the supplied code chunk does not implement any observable functionality at all. It is only a placeholder init.py file for a utils package. Based on this code alone, the described purpose is not supported, so this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger rules are extremely broad and include common keywords like '画图', '同步', '飞书', and '账本', plus an instruction that the skill 'must' be used whenever they appear. This can cause unintended invocation in unrelated conversations, leading the agent to access or modify sensitive financial data, read local ledger files, or initiate export/sync actions when the user did not actually intend bookkeeping operations.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill advertises CSV export, cloud sync, and file upload capabilities for personal financial records without corresponding user-facing privacy warnings, consent requirements, or destination/retention details. In this context, the data is highly sensitive, so ambiguous export/sync behavior materially increases the risk of unintended disclosure of transactions, balances, and account metadata.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · SKILL.md (reported line 60)May include surrounding context.

bash
# 创建账本
uv run python ~/.openclaw/skills/ledger/src/cli.py create --name 新账本

# 列出账本
uv run python ~/.openclaw/skills/ledger/src/cli.py list

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · SKILL.md (reported line 60)May include surrounding context.

bash
# 创建账本
uv run python ~/.openclaw/skills/ledger/src/cli.py create --name 新账本

# 列出账本
uv run python ~/.openclaw/skills/ledger/src/cli.py list

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This markdown file presents the skill architecture, interface descriptions, and CLI examples exclusively in Chinese. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly documented and justified, which is not present here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This Python skill contains user-facing natural-language content exclusively in Chinese, including the module docstring and usage examples. The policy requires flagging language or locale constraints when a skill forces a specific language without user opt-in, and no alternate language option or justification is provided here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The comment and logic explicitly treat ASCII as 'English' and apply normalization only to those names. This creates language-dependent behavior in the skill without any documented user choice or justification, which matches the language/locale policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This Python file contains natural-language strings entirely in Chinese, including the module docstring and all user-facing print output. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is clearly documented and justified, which is not present here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This Python file contains natural-language docstrings and all console messages exclusively in Chinese, including the title, status, and error output. Under the policy rule for language/locale, this is a violation because the skill does not offer a language option or explain that it is intentionally limited to a Chinese-speaking or region-specific context.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This Python file contains user-facing natural-language text in Chinese, beginning with the module description and continuing throughout print statements and docstrings. Under the policy rule for natural-language violations, forcing a specific language without user opt-in or a documented regional justification is a policy concern.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The inline comment at L065 states that all transactions are imported, including opening balances treated as ordinary expenses. However, the code simply forwards each JSONL record to add_transaction using its existing fields and never invokes the imported set_opening_balance function or any logic that transforms opening-balance records into a specific representation. This is a direct documentation-to-code mismatch in migration intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module description is written entirely in Chinese, and all user-facing docstrings throughout the file are also Chinese-only. Under the policy, a skill should not impose a specific language or locale without opt-in or a clear documented regional justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This Python file contains user-facing title text, status messages, and tabular labels entirely in Chinese, indicating the skill is designed to operate in a fixed language. The file does not provide any user opt-in, locale selection, or documentation justifying a Chinese-only constraint, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The file’s natural-language content is written entirely in Chinese, beginning with the module description on L002 and continuing through docstrings/comments, with no indication that language selection is optional or that the skill is intentionally region-specific. This can violate the language/locale policy when a skill implicitly enforces one language without user opt-in.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The docstring for get_ledger_date_range says it returns earliest and latest months in the form {'start': '2025-01', 'end': '2026-03'}, but the implementation returns raw MIN(date) and MAX(date) values from the database. The inline comment at L206 also confirms the function actually returns full YYYY-MM-DD dates, creating a direct documentation-to-code contradiction.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

This code file contains user-facing natural-language docstrings and comments entirely in Chinese, including the function descriptions and supported input examples. Under the policy rule, a skill that effectively constrains interaction to a specific language without opt-in or justification can be a locale-policy violation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.