Back to skill

Security audit

发票识别(invoice-discern) - 慧穗云

Security checks for vulnerabilities and agentic risk

Overview

This invoice OCR skill does what it claims, but it can send sensitive invoices and authentication material to any API host configured in the environment.

Review before installing. Use this only with invoices you are allowed to submit to Huisuiyun, set HSY_API_URL only to the trusted HTTPS service, protect HSY_AK and HSY_SK, and avoid running it in environments where another party can control environment variables.

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

Error
Location
invoice-discern.py:14
Finding
Unrestricted API Endpoint Allows Disclosure of Credentials and Invoice Data## Vulnerability Details **File Location**: `invoice-discern.py`, lines 14–25 and 33–62 **Vulnerability Type**: Unvalidated destination URL for sensitive network requests **Risk Level**: High ### Vulnerable Code ```python def get_token(api_url, ak, sk, type_value): """获取访问令牌""" token_url = f"{api_url}/api/v2/agent/common/cdk/getToken" secret_string = get_md5(ak + sk) payload = { "akString": ak, "secretString": secret_string, "type": int(type_value), "forceUpdate": 0 } try: response = requests.post(token_url, json=payload) result = response.json() if result.get("code") == "200": return result["data"] else: return None, result.get("message", "获取Token失败") except Exception as e: return None, str(e) ``` ```python def discern_invoice(file_path, tax_no=None): """识别发票""" api_url = os.getenv("HSY_API_URL", "https://huisuiyun.com") ak = os.getenv("HSY_AK") sk = os.getenv("HSY_SK") type_value = os.getenv("HSY_TYPE", "2") # ... headers = {"X-Access-Token": token} if type_value == "1" and tax_no: headers["X-Tax-Token"] = tax_no discern_url = f"{api_url}/api/v2/agent/cdk/invoice/discern" try: with open(file_path, 'rb') as f: files = {'file': f} response = requests.post(discern_url, files=files, headers=headers) return response.json() except Exception as e: return {"error": str(e)} ``` ### Technical Analysis The destination for both authentication and invoice-upload requests is controlled through the `HSY_API_URL` environment variable. The implementation concatenates API paths onto this value without validating its scheme, hostname, port, user-information component, or final destination. Consequently, the value may point to an arbitrary HTTP or HTTPS ...[truncated 3298 chars]
Remediation
## Remediation Suggestions 1. **Pin the production API origin.** Use a constant such as `https://huisuiyun.com` rather than accepting an unrestricted environment-provided URL. ```python API_ORIGIN = "https://huisuiyun.com" ``` 2. **If endpoint configurability is operationally necessary, enforce an explicit allowlist.** Parse the URL and require: - The `https` scheme. - An exact approved hostname. - No username or password component. - No fragments. - Only an approved port, normally `443`. ```python from urllib.parse import urlparse ALLOWED_HOSTS = {"huisuiyun.com"} def validate_api_url(value): parsed = urlparse(value) if ( parsed.scheme != "https" or parsed.hostname not in ALLOWED_HOSTS or parsed.username is not None or parsed.password is not None or parsed.port not in (None, 443) or parsed.fragment ): raise ValueError("Unapproved HSY API endpoint") return f"https://{parsed.hostname}" ``` 3. **Do not silently permit development endpoints in production.** If testing against alternate servers is required, place that capability behind an explicit development-only option and use a separate set of non-production credentials. 4. **Set connection and response timeouts** on both requests to limit denial-of-service exposure: ```python response = requests.post(token_url, json=payload, timeout=(5, 30)) response.raise_for_status() ``` 5. **Validate authentication responses strictly.** Confirm the HTTP status, response content type, expected JSON structure, and token type before using the returned value. 6. **Avoid redirecting sensitive requests to unapproved origins.** Disable redirects or validate every redirect destination before forwarding credentials or invoice data: ```python requests.post(..., allow_redirects=False) ``` 7. ...[truncated 555 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • 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 (8)

Tainted flow: 'discern_url' from os.getenv (line 64, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
try:
        with open(file_path, 'rb') as f:
            files = {'file': f}
            response = requests.post(discern_url, files=files, headers=headers)
        return response.json()
    except Exception as e:
        return {"error": str(e)}
Confidence
95% confidence
Finding
The destination URL is derived from the HSY_API_URL environment variable and is used directly in requests.post while sending the invoice file and access token headers. In a hostile or misconfigured environment, this allows exfiltration of sensitive invoice documents and authentication material to an attacker-controlled endpoint, making the issue more serious because this skill’s core purpose handles financial documents.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill requires environment secrets and makes networked API calls, but it does not declare an explicit tool scope such as permissions or allowed-tools. This weakens governance and user awareness because the agent may access credentials and transmit data externally without a clearly documented capability boundary.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The skill processes invoice images and extracted invoice fields through a third-party API, but the description does not clearly warn users that potentially sensitive financial and personal data will leave the local environment. This creates a meaningful privacy and compliance risk because users may unknowingly submit confidential invoice contents, tax IDs, names, routes, and other billing data to an external processor.

External Transmission

Medium
Category
Data Exfiltration
Content
}

    try:
        response = requests.post(token_url, json=payload)
        result = response.json()
        if result.get("code") == "200":
            return result["data"]
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The code uploads the provided invoice file to a remote API with no in-code confirmation, warning, or transparency to the caller about external transmission. Because invoices commonly contain sensitive business and personal data, undisclosed remote upload creates privacy, compliance, and data-handling risk even if it is part of the intended functionality.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The natural-language instructions and usage guidance are effectively restricted to Chinese, which can amount to a language policy issue when no user opt-in or alternative language is provided. The file does not state that the skill is intentionally limited to Chinese-speaking users or a China-specific compliance context.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
User-facing docstrings and error/help messages are written in Chinese only, including setup guidance and usage-related text. This imposes a specific language on users without any option to select another locale or indication that the skill is intended solely for a Chinese-language context.

Missing User Warnings

Low
Confidence
81% confidence
Finding
The skill accesses HSY_AK and HSY_SK from environment variables to authenticate with the external service. The code includes an error message when the variables are missing, but it does not clearly disclose in comments, docs, or runtime output that the skill uses these sensitive credentials for remote authentication.

Static analysis

No suspicious patterns detected.