Back to skill

Security audit

purchase-record

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent purchase-recording tool, but its command handler can let crafted purchase text run unintended operating-system commands.

Install only if you trust the environment and can review or patch the skill first. The shell-based launcher should be changed to use argument-based process execution, item names should be sanitized before writing to Excel, and users should confirm the exact workbook path before allowing writes.

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/index.js:10
Finding
OS Command Injection Through Shell-Based Python Invocation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/index.js`, lines 1 and 10-14; user-controlled data reaches the vulnerable function at line 59 **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```javascript const { exec } = require('child_process'); function executePython(message) { return new Promise((resolve, reject) => { const pythonCmd = `python "${PYTHON_SCRIPT_PATH}" "${message.replace(/"/g, '\\"')}"`; exec(pythonCmd, (error, stdout, stderr) => { if (error) { console.error(`执行错误:${error.message}`); if (stderr) { console.error(`Stderr: ${stderr}`); } ``` The command handler passes agent-controlled command text to this function: ```javascript if (lowerCmd.startsWith('采购')) { try { const result = await executePython(lowerCmd); return { reply: result }; ``` ### Technical Analysis The Skill constructs a command-line string containing the untrusted `message` value and executes it using `child_process.exec()`. Unlike process APIs that accept an executable and argument array, `exec()` invokes a command shell. The attempted escaping operation: ```javascript message.replace(/"/g, '\\"') ``` does not provide reliable shell quoting. In particular, backslash is not a general escape character for quotation marks under Windows `cmd.exe`. An attacker can introduce a quotation mark to terminate the intended argument and then supply shell control operators. The shell interprets these operators before `add_purchase.py` can validate the purchase command. The `startsWith('采购')` check is not a security boundary. An input can begin with the required prefix while still containing shell syntax later in the string. ### Attack Path 1. An attacker submits a command beginning with the accepted `采购` prefix. 2. The complete attacker-controlled string is passed to `executePython()`. 3. A quotation ...[truncated 1376 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Eliminate shell interpretation by replacing `exec()` with `execFile()` or `spawn()` and passing arguments separately: ```javascript const { execFile } = require('child_process'); function executePython(message) { return new Promise((resolve) => { execFile( 'python', [PYTHON_SCRIPT_PATH, message], { shell: false, windowsHide: true, timeout: 10000, maxBuffer: 1024 * 1024 }, (error, stdout, stderr) => { // Handle the result without constructing a shell command. } ); }); } ``` 2. Resolve the Python script relative to the installed Skill directory rather than using a hard-coded, user-specific path: ```javascript const PYTHON_SCRIPT_PATH = path.join(__dirname, 'add_purchase.py'); ``` 3. Validate the command before starting another process: - Enforce a reasonable maximum input length. - Require the documented purchase-command structure. - Reject control characters and unexpected line breaks. - Validate the date, item name, and price as separate fields. 4. Run the Skill under a least-privileged account with access only to the required workbook and Skill files. 5. Do not attempt to repair this issue by adding more shell escaping. Avoiding the shell entirely is the robust mitigation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/record.py:78
Finding
Spreadsheet Formula Injection Through Untrusted Item Names<![CDATA[ ## Vulnerability Details **File Location**: `scripts/record.py`, lines 78-80 and 101-107; input is accepted by `scripts/main.py`, lines 27-38 and 59-65 **Vulnerability Type**: Spreadsheet formula injection **Risk Level**: Medium ### Vulnerable Code The alternate command handler accepts arbitrary content as the item name: ```python pattern = r'^(\d{4})\s+(.+?)\s+(\d+\.?\d*)(?:元)?' match = re.match(pattern, params) if not match: return None date_str = match.group(1) name = match.group(2).strip() price_str = match.group(3) ``` It then passes that value to the workbook writer: ```python date_str, name, price = result # Directly import and call the recording function from record import record_purchase try: record_purchase(date_str, name, price) ``` The workbook writer stores the item name without neutralizing spreadsheet formulas: ```python # Excel file path excel_path = Path.home() / "Desktop" / "purchase_record.xlsx" # Find the last row containing data last_row, sheet_name = find_last_data_row(wb) write_row = last_row + 1 ws = wb[sheet_name] # Write data ws.cell(row=write_row, column=1).value = date ws.cell(row=write_row, column=2).value = name ws.cell(row=write_row, column=3).value = price ``` A comparable unneutralized assignment is also present in `scripts/add_purchase.py` at line 87: ```python ws.cell(row=first_empty_row, column=2, value=data['item_name']) ``` ### Technical Analysis Spreadsheet applications and libraries commonly interpret strings beginning with `=` as formulas. Other spreadsheet import paths may also treat values beginning with `+`, `-`, or `@` as formulas. The item-name parser accepts arbitrary non-empty content and does not reject or neutralize formula prefixes. `record_purchase()` then assigns the value directly to an `openpyxl` cell. When a string begins with `=`, `openpyxl` can serialize it as a formula rather than inert text. The vulnerable value is not evaluated by Python itself. The dangerous beha ...[truncated 1566 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat every item name as untrusted spreadsheet content. 2. Reject item names beginning with formula-significant characters after trimming leading whitespace: ```python def validate_item_name(name: str) -> str: value = name.strip() if not value: raise ValueError("The item name cannot be empty") if value[0] in ("=", "+", "-", "@"): raise ValueError("The item name cannot begin with a spreadsheet formula marker") return value ``` 3. If such names must be supported, force them to be stored as literal text. A common defense is to prefix the value with an apostrophe and explicitly use a text number format: ```python safe_name = name.strip() if safe_name.startswith(("=", "+", "-", "@")): safe_name = "'" + safe_name cell = ws.cell(row=write_row, column=2) cell.value = safe_name cell.number_format = "@" ``` 4. Apply the same centralized sanitizer to every workbook-writing implementation, including: - `scripts/record.py` - `scripts/add_purchase.py` - `scripts/add_purchase.js` 5. Account for leading tabs, carriage returns, line feeds, and other whitespace that could precede a formula marker. Validation should inspect the normalized value rather than only its first raw character. 6. Add automated tests confirming that formula-like item names are rejected or serialized as literal text and are not represented as formula cells after the workbook is reloaded. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (18)

Ae1

High
Category
analysis-evasion
Content
- **主要功能**: scripts/index.js
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Natural-Language Policy Violations

Medium
Confidence
84% confidence
Finding
All user-facing instructions in the README are presented in Chinese, and the file does not indicate that the language is optional or limited to a specific audience. This can violate a language/locale policy when a skill forces a specific language without user opt-in.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill explicitly states it will automatically append user-provided purchase data into a local Excel file, but it does not clearly warn the user that running the skill causes a persistent modification to a specific filesystem location. This can mislead users about side effects, reduce informed consent, and increase the chance of unintended data alteration or corruption on the host machine.

Vague Triggers

Medium
Confidence
84% confidence
Finding
The skill description says it will automatically write procurement information into Excel but does not define clear activation conditions, user confirmation requirements, or scope boundaries. This increases the chance the agent could trigger in unintended contexts and perform file-modifying actions on sensitive business data without sufficiently explicit user intent.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The description is written only in Chinese, which can bias the skill toward a fixed language context without explicit user opt-in. While not directly enabling code execution or data theft, it can cause user confusion, misinterpretation of actions, and accidental invocation in multilingual environments, which is more concerning here because the skill performs automatic writes to Excel.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The script performs a local file write to a fixed, hard-coded path without any runtime confirmation, disclosure, or path validation. In an agent-skill context, this is dangerous because invoking the skill causes side effects on the host filesystem, potentially modifying a sensitive workbook unexpectedly or enabling unauthorized record manipulation if users are unaware of the target file.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The skill constructs a shell command with user-influenced input and executes it via child_process.exec, which invokes a shell. Escaping only double quotes is insufficient on Windows cmd.exe because shell metacharacters can still alter command execution, creating a command-injection risk in addition to delegating behavior to an opaque external script.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
User-controlled command content is forwarded into a subprocess without meaningful confirmation or safety controls. Because the subprocess is launched through a shell, the absence of validation and explicit warning increases the chance of unintended command execution or dangerous side effects from crafted input.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The code indicates that an Excel file may be automatically created if it does not exist, which is a file-system modification affecting user data. This behavior is only revealed after the operation path and is not clearly warned in advance in comments, prompts, or the help description.

Missing User Warnings

Low
Confidence
92% confidence
Finding
The README states that the tool will automatically write purchase information into an Excel file and append data to the first non-blank row, but it does not include a clear user warning about modifying local data. For a markdown skill description, file-changing behavior that affects user data should be disclosed explicitly so users understand the impact before invoking the skill.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The manifest description is written entirely in Chinese ('采购记录管理 - 自动将采购信息写入 Excel') and does not indicate any user language choice or explain that the skill is intended only for a Chinese-language or region-specific context. Under the policy, forcing a specific language or locale without opt-in can be a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The docstrings, usage text, and command format all require Chinese-language input such as the fixed trigger word '采购'. This imposes a specific language on users without any opt-in, language selection, or justification that the skill is intended only for a Chinese-language context.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
The module docstring states that purchase information will be '追加到 Excel 表格' (appended to the Excel sheet). However, the implementation scans for the first completely empty row starting at row 2 and writes there, which can insert into gaps in the middle of the sheet instead of appending after the last existing record. This is a direct mismatch between the documented behavior and the actual write semantics.

Intent-Code Divergence

Low
Confidence
99% confidence
Finding
The `add_to_excel` docstring says '找到第一非空白行并写入数据' (find the first non-blank row and write data), but the loop explicitly checks that all three cells are `None` and selects that row only when it is blank. This is an active contradiction in the inline documentation of the function's behavior.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The command handling and user-facing responses are written only in Chinese, and the trigger expects the Chinese word '采购'. There is no indication that the skill is region-specific or that users can opt into this locale, which can violate language/locale policy requirements.

Intent-Code Divergence

Low
Confidence
76% confidence
Finding
The inline help describes usage for recording purchases, but the success-path messaging shows the underlying operation may automatically initialize or create an Excel file. This is more than incomplete formatting help: it omits a meaningful write-side effect while presenting the skill as a straightforward record command.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
This Python file contains natural-language documentation and command/help text entirely in Chinese, including the required command format and error messages. Under the policy rule for language/locale, this can be considered a forced language experience because the skill does not offer any user opt-in or alternative locale handling.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The script's console messages and example inputs are entirely in Chinese, indicating a fixed language choice for user-facing interaction. For a general-purpose skill file, this is a natural-language locale constraint without any visible opt-in, selection mechanism, or documented justification.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/index.js:14