Back to skill

Security audit

票IN 发票

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its invoice-sync purpose, but it asks the agent to receive and persist an API key while handling sensitive invoice data with weak local protections.

Review before installing. Use this only if you are comfortable giving the agent access to Piaoin invoice records and uploads. Prefer setting the API key through a local environment variable or secure secret store instead of pasting it into chat, keep .piaoin_evn out of source control, verify its file permissions and BASE_URL value, and confirm tenant-wide downloads only when the key belongs to an authorized tenant administrator.

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

Warning
Location
scripts/piaoin_invoice.py:39
Finding

API Key Stored in Plaintext Without Restrictive File Permissions

Content
View full analysis
Path: path = env_path or (root / ENV_FILE) order = ["API_KEY", "BASE_URL", "USER_ROLE", "LAST_SYNC_DATE", "RUNTIME_MODE", "LAST_RUNTIME"] lines = [] for key in order: if key in data and data[key] != "": lines.append(f"{key}={data[key]}") for key in sorted(k for k in data if k not in order): lines.append(f"{key}={data[key]}") path.write_text("\n".join(lines) + "\n", encoding="utf-8") return path ``` ### Technical Analysis The `save_env()` function serializes the `API_KEY` into `.piaoin_evn` as plaintext. The file is created through `Path.write_text()` without explicitly setting owner-only permissions, validating ownership, or repairing unsafe permissions on an existing file. Consequently, the effective protection depends on the operating system, workspace configuration, and process umask. In a shared environment or workspace with permissive permissions, another local account or process may be able to read the credential. The state file could also be inadvertently committed to source control, copied into an artifact, or included in a backup. This behavior exceeds the minimum privilege necessary because persistent plaintext storage is not essential to making authenticated API requests. The credential could instead be supplied through an environment variable or protected credential store. ### Attack Path 1. A user runs the configuration command with a valid PAT, such as `config --api-key py_xxx`. 2. The application writes the complete PAT into `.piaoin_evn`. 3. The resulting file has permissions determined by the surrounding environment rather than explicit owner-only controls. 4. Another local user, p ...[truncated 875 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/piaoin_invoice.py:93
Finding

Editable BASE_URL Can Redirect API Credentials and Invoice Data to an Arbitrary Host

Content
View full analysis
str: return (env.get("BASE_URL") or DEFAULT_BASE_URL).rstrip("/") def role(env: dict[str, str]) -> str: return env.get("USER_ROLE", "unknown") def mask_key(key: str) -> str: if len(key) <= 10: return "***" return f"{key[:3]}...{key[-4:]}" def http_json(method: str, url: str, api_key: str, body: bytes | None = None, headers: dict[str, str] | None = None) -> dict: request_headers = {"X-API-Key": api_key, "Accept": "application/json"} if headers: request_headers.update(headers) ``` The unvalidated value is used for list requests: ```python query = urllib.parse.urlencode(params) url = f"{base_url(env)}/api/v1/pat/invoices/list?{query}" return http_json("GET", url, require_api_key(env)) ``` It is also used when transmitting local invoice files: ```python body, ctype = encode_multipart({}, {"file": file_path}) url = f"{base_url(env)}/api/v1/pat/invoices/upload" payload = http_json("POST", url, require_api_key(env), body=body, headers={"Content-Type": ctype}) print_upload_result(payload) ``` ### Technical Analysis The command-line `--base-url` option is restricted to the production and test Piaoin origins. However, `load_env()` accepts arbitrary key-value content from the editable `.piaoin_evn` or legacy `.piaoin_env` file, and `base_url()` returns the stored `BASE_URL` without validating its scheme, hostname, port, user information, or origin. `http_json()` unconditionally attaches the sensitive `X-API-Key` header to the resulting URL. During local-file uploads, the multipart request body also contains the complete invoice document. Therefore, a malicious or compromised state file can redirect both credentials and invoice data to ...[truncated 1841 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (12)

Ssd 3

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

This section explicitly tells the user to copy a one-time API secret and send it to the agent, then save it locally for future use. That is a direct natural-language prompt to disclose credentials to an automated intermediary, creating unnecessary secret exposure and increasing the blast radius if logs, transcripts, or local files are later accessed.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill explicitly instructs the agent to ask the user to paste their API secret into chat, which creates a clear secret-handling risk. Chat transcripts, logs, analytics pipelines, or downstream tools may retain the credential, enabling account misuse and unauthorized invoice access/upload actions.

Content

No source excerpt is available for this finding.

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · references/pat-api.md (reported line 44)May include surrounding context.

md
1. 线上 Agent 有请求工具时,直接用请求工具发送 HTTP 请求。
2. 本地有 Python 时,使用 `scripts/piaoin_invoice.py`。
3. 无 Python 但有 curl 时,使用 curl。
4. Windows 无 Python 但可用 PowerShell 时,使用 `Invoke-RestMethod` 查询、`curl.exe -F` 上传。
5. 无可用 HTTP 客户端时,只生成请求命令,不标记任务完成。

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · references/pat-api.md (reported line 44)May include surrounding context.

md
1. 线上 Agent 有请求工具时,直接用请求工具发送 HTTP 请求。
2. 本地有 Python 时,使用 `scripts/piaoin_invoice.py`。
3. 无 Python 但有 curl 时,使用 curl。
4. Windows 无 Python 但可用 PowerShell 时,使用 `Invoke-RestMethod` 查询、`curl.exe -F` 上传。
5. 无可用 HTTP 客户端时,只生成请求命令,不标记任务完成。

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill instructs use of sensitive capabilities including environment access, file reads/writes, network requests, and shell execution, but it does not declare an explicit tool scope or permission boundary. That increases the chance an agent will execute high-impact operations without clear least-privilege constraints or user visibility into what the skill is allowed to do.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The example state file stores a full API key alongside runtime metadata in the same plaintext file, encouraging aggregation of credentials and operational context in one place. Combining secrets with environment details increases the chance of accidental disclosure and makes any leak more useful to an attacker.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill directs the agent to persist the API key in plaintext inside a local state file, but does not prominently warn users about the credential storage risk or recommend a safer secret store. Plaintext secrets in project directories are commonly exposed through backups, source control, shared workspaces, or accidental file inspection.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The document instructs the agent to present a specific Chinese message to the user when no API key is available, and the overall skill content is written as mandatory Chinese-facing guidance. There is no indication that the user can choose another language or that the Chinese-only behavior is limited to a justified region-specific compliance context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script persists the API key in a plaintext local file (.piaoin_evn/.piaoin_env) without setting restrictive permissions or warning the user that credentials are being stored on disk. On multi-user systems, shared workspaces, backups, or accidentally committed project directories, this can expose a credential that grants access to invoice data and upload/download operations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The download flow writes remotely retrieved invoice files and structured invoice metadata to local disk automatically, including potentially sensitive billing information, without any explicit confirmation, privacy notice, or controls over storage location/protection. Because invoice data commonly contains personal, financial, and tenant-wide business information, silent persistence increases the risk of local data leakage, accidental sharing, and retention beyond user intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The upload command reads a local invoice file and sends it to the remote API endpoint. The command name implies upload behavior, but the file lacks a clear user-facing warning or comment that document contents will be transmitted to an external service, which is relevant for privacy-sensitive invoice data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The display name, description, and prompt are entirely in Chinese, but the file does not indicate that the skill is region-specific or that users can opt into this language. This can violate language/locale policy when a skill imposes a specific language without documented justification or user choice.

Content

No source excerpt is available for this finding.