Back to skill

Security audit

Receipt Assistant

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent receipt-processing purpose, but it needs review because unsafe filename and report handling can write outside the intended output folder or create risky Excel content.

Review before installing. Use this only on trusted receipt folders, set a dedicated output directory, avoid running it with elevated privileges, and treat generated spreadsheets as containing untrusted data. The publisher should add filename sanitization, output-directory confinement checks, Excel formula neutralization, clearer confirmations, and pinned dependencies.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/namer.py:87
Finding

Unrestricted File Write Through Path Traversal and Absolute Paths

Content
View full analysis

Vulnerability Details

File Location: scripts/namer.py:39-55, scripts/namer.py:87-123, and scripts/reporter.py:21-58
Vulnerability Type: Path traversal and unrestricted file write
Risk Level: High

Vulnerable Code

python
def _generate_train_name(self, ticket: TicketInfo) -> str:
    """火车票命名: {person}-{date}-{amount}-中国铁路"""
    person = ticket.person or "未知"
    date = ticket.date or "未知"
    amount = ticket.amount or "未知"
    ext = Path(ticket.original_name).suffix
    return f"{person}-{date}-{amount}-中国铁路{ext}"

def _generate_didi_name(self, ticket: TicketInfo) -> str:
    """打车发票命名: {company}-{date}-{invoice_no}-{amount}-{seller}-发票"""
    company = COMPANY_NAME
    date = ticket.date or "未知"
    invoice_no = ticket.invoice_no or "未知"
    amount = ticket.amount or "未知"
    seller = ticket.seller or "未知"
    ext = Path(ticket.original_name).suffix
    return f"{company}-{date}-{invoice_no}-{amount}-{seller}-发票{ext}"
python
def rename_file(self, ticket: TicketInfo, create_subdir: bool = True) -> TicketInfo:
    """重命名并移动文件"""
    if ticket.status != ProcessStatus.SUCCESS:
        return ticket

    # 生成新文件名
    new_name = self.generate_name(ticket)
    ticket.new_name = new_name

    # 确定输出子目录
    if create_subdir:
        type_dir = self.output_dir / ticket.file_type.value
        type_dir.mkdir(parents=True, exist_ok=True)
        output_path = type_dir / new_name
    else:
        output_path = self.output_dir / new_name

    # 处理文件名冲突
    if output_path.exists():
        base_name = Path(new_name).stem
        ext = Path(new_name).suffix
        counter = 1
        while output_path.exists():
            new_name = f"{base_name}_{counter}{ext}"
            output_path = (type_dir if create_subdir else self.output_dir) / new_name
            counter += 1
        ticket.new_name = new_name

    # 复制文件(原文件保留)
    try:
      
...[truncated 3994 chars]
Remediation
View remediation

Remediation Suggestions

  1. Validate every filename component against a strict allowlist appropriate for dates, amounts, invoice numbers, personal names, and seller names.

  2. Replace or reject /, \, null bytes, drive prefixes, UNC prefixes, and parent-directory components.

  3. Reject absolute paths and require report filenames to be plain basenames with an approved .xlsx extension.

  4. Resolve both the output root and candidate destination before writing, then verify confinement:

    python
    output_root = self.output_dir.resolve()
    destination = (output_root / safe_name).resolve()
    
    if output_root not in destination.parents:
        raise ValueError("Destination escapes the configured output directory")
    
  5. Use Path(value).name only as an additional safeguard, not as a substitute for validation.

  6. Build names from individually sanitized components and enforce a maximum filename length.

  7. Open destination files using exclusive-creation semantics where feasible to prevent unintended overwrites and race conditions.

  8. Apply the same confinement validation to ReportGenerator.generate_excel().

  9. Add tests for absolute POSIX paths, Windows drive paths, UNC paths, mixed separators, nested traversal sequences, and encoded or Unicode separator variants.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/reporter.py:34
Finding

Excel Formula Injection Through Receipt and Filename Fields

Content
View full analysis

Vulnerability Details

File Location: scripts/reporter.py:34-49
Vulnerability Type: Spreadsheet formula injection
Risk Level: Medium

Vulnerable Code

python
# 写入数据
for idx, ticket in enumerate(tickets, start=1):
    row = [
        idx,
        ticket.file_type.value,
        ticket.original_name,
        ticket.new_name,
        ticket.date,
        ticket.invoice_no,
        ticket.amount,
        ticket.seller,
        ticket.buyer,
        ticket.person,
        ticket.status.value,
    ]
    ws.append(row)

Technical Analysis

The report generator writes OCR-derived and filename-derived strings directly into XLSX cells. Relevant untrusted fields include original_name, new_name, invoice_no, seller, buyer, and person.

Spreadsheet applications interpret certain leading characters as formula indicators. In particular, openpyxl normally stores strings beginning with = as formula cells. Other spreadsheet clients and import workflows may also interpret values beginning with +, -, or @ as formulas. Leading tabs, carriage returns, or newlines can be used to bypass incomplete checks.

The code neither neutralizes formula-leading values nor explicitly stores untrusted fields as literal text. Consequently, crafted receipt metadata or filenames can become executable spreadsheet expressions when a user opens the generated report.

Attack Path

  1. An attacker controls a receipt filename or embeds a crafted value in a receipt field likely to be extracted by OCR, such as the seller or passenger name.
  2. The value begins with a spreadsheet formula indicator, for example an equals sign followed by an attacker-selected formula.
  3. The processing workflow preserves the value in a TicketInfo field.
  4. generate_excel() passes it directly to ws.append().
  5. openpyxl stores the value as a formula rather than inert text.
  6. A victim opens the generated ...[truncated 935 chars]
Remediation
View remediation

Remediation Suggestions

  1. Treat every filename, OCR result, and caller-supplied report field as untrusted text.

  2. Before writing a string to a cell, detect values whose first effective character is =, +, -, or @, including values prefixed by tabs, carriage returns, or newlines.

  3. Prefix dangerous values with an apostrophe or otherwise force the cell to use a literal text data type.

  4. Centralize this behavior in a helper and apply it to every untrusted column:

    python
    def safe_excel_text(value):
        if not isinstance(value, str):
            return value
    
        dangerous = ("=", "+", "-", "@", "\t", "\r", "\n")
        if value.startswith(dangerous):
            return "'" + value
    
        return value
    
  5. Where appropriate, set the cell data type explicitly to text rather than relying only on string transformation.

  6. Do not remove ordinary characters from legitimate values unnecessarily; preserve the original data in a safely encoded form.

  7. Add tests confirming that formula-leading seller names, passenger names, invoice numbers, and filenames are stored as literal text and are not represented as formula cells in the resulting workbook.

  8. Document that generated reports contain untrusted receipt-derived data and should be opened using protected-view controls.

Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (18)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill describes filesystem-affecting behavior such as scanning directories, renaming files, generating Excel reports, and using configuration, but it does not declare an explicit tool scope or permissions boundary. This creates an authorization ambiguity where the runtime may grant broader file or environment access than users expect, increasing the risk of unintended data exposure or modification.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The trigger phrases include very common terms such as '报销' and '发票', which can match ordinary conversation and cause the skill to activate unintentionally. Because the skill can scan directories and rename files, accidental invocation could lead to unexpected file operations or processing of sensitive receipts without clear user intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

All user-facing description and prompt content are written only in Chinese, and the skill does not indicate that another language can be used or selected. Under the stated policy, a skill should not impose a specific language or locale without user opt-in or documented justification.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The workflow states that the skill will scan directories, rename files, and generate output files, but it does not clearly warn the user up front that it will modify the filesystem. Users may provide a path expecting read-only analysis and instead experience irreversible renames, overwritten outputs, or processing of confidential financial documents.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The update_config function modifies company name, person name, and output directory values, then persists them via save_config. Although this is part of configuration management, this file provides no comment, docstring detail, or CLI disclosure that user-supplied personal and filesystem information will be saved.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code file contains user-/developer-facing natural-language text exclusively in Chinese, including the module docstring, enum values, comments, and dictionary keys. Under the stated policy, forcing a specific language without user opt-in is a locale/language policy violation unless the 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
94% confidence
Finding

This Python file contains natural-language docstrings, comments, and error messages exclusively in Chinese, including the module description and runtime error text. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly 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

The file’s natural-language strings and docstrings consistently specify and present the skill entirely in Chinese, including the module description and worksheet title, with no indication that language is configurable or chosen by the user. This can violate a language/locale policy when a skill imposes a specific language without opt-in or explicit justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The file contains a Chinese-language comment ('报销助手 - 数据模型') with no indication that language selection is optional or that the skill is intentionally limited to a Chinese-speaking or region-specific context. Under the policy, forcing or assuming a specific language without user opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

Docstrings and CLI usage strings in this file are written only in Chinese, with no mechanism for language selection or indication that the skill is intentionally limited to a Chinese-speaking audience. This can violate language or locale policy when users are not given a choice.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

技能清单描述聚焦于票据识别、关键信息提取、按规则重命名文件和生成汇总报表,但此文件还暴露了检查与更新配置的接口,包括公司名、个人姓名和输出目录。虽然这些配置可能服务于报销流程,但它们属于未在描述中明确声明的额外管理能力。

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency is specified with a lower bound only, which allows future installs to resolve to different versions over time. This weakens reproducibility and can unintentionally introduce vulnerable or incompatible releases through the supply chain, even if no specific exploit is present in the file itself.

Content

Scanner excerpt · scripts/requirements.txt (reported line 1)May include surrounding context.

text
openpyxl>=3.1.0
pdfplumber>=0.10.0
Pillow>=10.0.0

Unverifiable Dependency: openpyxl has 2 known advisory(ies) (CVE-2017-5992 (Improper Restriction of XML External Entity Reference in Openpyxl); CVE-2017-5992 (Openpyxl 2.4.1 resolves external entities by default, which allows remote attack)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
87% confidence
Finding

The manifest does not pin openpyxl, and the package has known historical advisories including XXE-related issues. Because the exact installed version cannot be verified, there is a real risk that a vulnerable release could be installed, though the currently declared minimum version may or may not already exclude the affected versions.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

Using an unpinned version range for pdfplumber means installations are not deterministic and may pull newer transitive code without review. This increases supply-chain risk and makes it harder to verify whether deployed environments are using a safe, tested version.

Content

Scanner excerpt · scripts/requirements.txt (reported line 2)May include surrounding context.

text
openpyxl>=3.1.0
pdfplumber>=0.10.0
Pillow>=10.0.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

Pillow is also unpinned, so environments may resolve to different versions, including versions later found vulnerable or incompatible. In this skill's context, image processing of user-supplied receipts makes dependency hygiene more important because parsing libraries often handle complex, attacker-controlled file formats.

Content

Scanner excerpt · scripts/requirements.txt (reported line 3)May include surrounding context.

text
openpyxl>=3.1.0
pdfplumber>=0.10.0
Pillow>=10.0.0

Unverifiable Dependency: Pillow has 16 known advisory(ies) (CVE-2016-2533 (Pillow buffer overflow in ImagingPcdDecode); CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2021-27922 (Pillow Uncontrolled Resource Consumption) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding

Pillow has a substantial history of security advisories, and the requirement is not pinned, so the deployed version cannot be confidently assessed. This is more concerning in a receipt-processing skill because it likely parses user-provided images, increasing exposure to malformed files that could trigger parser vulnerabilities such as denial of service or, in some versions, code execution.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code file contains natural-language strings such as the module docstring in Chinese, which effectively imposes a specific language on users or maintainers. Under the policy, language constraints should be optional or clearly justified as region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code file contains natural-language docstrings and comments exclusively in Chinese, including the module docstring and function descriptions. Under the stated policy, forcing a specific language without user opt-in can be a natural-language policy violation when no language choice or justification is provided.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.