Back to skill

Security audit

wooshpay-api

Security checks for vulnerabilities and agentic risk

Overview

This WooshPay payment skill is mostly coherent, but it has unsafe credential handling that could expose a merchant API key or payment secrets.

Review before installing. Use only with a restricted WooshPay key if possible, do not paste API keys into chat, avoid running get_payment.py with any full URL, and expect payment client secrets to appear in terminal output unless the skill is fixed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/get_payment.py:44
Finding
WooshPay API Credential Disclosure Through an Attacker-Controlled Lookup URL## Vulnerability Details **File Location**: `scripts/get_payment.py`, lines 44–45 and 68–73 **Vulnerability Type**: Arbitrary authenticated request, credential disclosure, and server-side request forgery **Risk Level**: High ### Vulnerable Code ```python # 补全完整URL if not order_id.startswith("http"): if not order_id.startswith("pi_"): print("❌ 订单ID格式错误,应以 pi_ 开头") sys.exit(1) return order_id ``` ```python # 构建URL if order_id.startswith("http"): url = order_id else: url = f"{BASE_URL}/{order_id}" print(f"\n⏳ 正在查询订单 {order_id}...") try: response = requests.get(url, headers=headers, timeout=30) ``` The `headers` object attached to this request contains the merchant credential: ```python headers = { "Authorization": f"Basic {api_key}" } ``` ### Technical Analysis The order lookup accepts any input beginning with `http` as a complete request URL. The script then sends a request to that URL while attaching the `WOOSHPAY_API_KEY` in an HTTP Basic Authorization header. There is no restriction requiring the destination to use HTTPS, no allowlist requiring the hostname to be `api.wooshpay.com`, and no strict validation that the input is a payment intent ID. Consequently, a user can be induced to enter an attacker-controlled URL instead of an order ID. Because the Authorization header is attached before the destination is validated, the merchant API key is disclosed directly to the selected server. An `http://` destination would additionally transmit it without transport encryption. The same behavior can access internal network addresses, creating an SSRF primitive. This behavior exceeds the minimum privileges required for payment status lookup. The declared functionality only requires requests to the fixed WooshPay endpoint. ### Attack Path 1. An attacker supplies a purported payment identifier such as `https://attacker.example/collect`. 2. A merchant ...[truncated 1305 chars]
Remediation
## Remediation Suggestions - Accept only payment intent identifiers, not complete URLs. - Validate input with a strict, length-bounded allowlist expression such as `^pi_[A-Za-z0-9]+$`. - Always construct the request URL from the trusted constant: ```python import re if not re.fullmatch(r"pi_[A-Za-z0-9]+", order_id): raise ValueError("Invalid payment intent ID") url = f"{BASE_URL}/{order_id}" response = requests.get(url, headers=headers, timeout=30) ``` - If configurable endpoints are required for testing, place them behind explicit configuration and verify that the parsed scheme is `https` and the hostname exactly matches an approved allowlist. - Never attach the API credential before validating the final destination. - Use a restricted API key with only read permission for status lookup if WooshPay supports scoped credentials. - Rotate the existing API key if the vulnerable lookup feature has been used with any untrusted URL.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/create_payment.py:156
Finding
Payment Client Secret Exposed in Terminal Output## Vulnerability Details **File Locations**: - `scripts/create_payment.py`, line 156 - `scripts/create_checkout.py`, lines 214–216 - `scripts/get_payment.py`, lines 90–91 **Vulnerability Type**: Sensitive payment credential exposure through standard output **Risk Level**: Medium ### Vulnerable Code `scripts/create_payment.py`: ```python print(f"📌 Client Secret: {result.get('client_secret')}") ``` `scripts/create_checkout.py`: ```python client_secret = result.get('client_secret') if client_secret: print(f"\n📌 Client Secret: {client_secret}") ``` `scripts/get_payment.py`: ```python if result.get('client_secret'): print(f"📌 Client Secret: {result.get('client_secret')}") ``` ### Technical Analysis Client secrets returned by WooshPay are printed directly to standard output. Terminal output may be retained in shell transcripts, continuous-integration logs, agent conversation history, support records, screen recordings, or centralized logging systems. Displaying the secret is not required for the declared status-query functionality and is not necessary when the operator only needs the hosted checkout or payment URL. The status lookup is particularly overprivileged because it discloses the secret during an otherwise read-only inspection operation. The precise authority of a WooshPay client secret depends on the service's implementation, but such secrets should be treated as sensitive bearer-style payment-session credentials and disclosed only to the client component that explicitly requires them. ### Attack Path 1. A legitimate creation or lookup request returns a `client_secret`. 2. The script writes the value to standard output without redaction. 3. The terminal session or agent execution output is captured in logs or conversation history. 4. An unauthorized person with access to those records retrieves the secret. 5. The person attempts to use it against the corresponding payment o ...[truncated 564 chars]
Remediation
## Remediation Suggestions - Remove all default printing of `client_secret` values. - For normal operations, display only non-sensitive identifiers, status information, and the hosted payment URL. - In `get_payment.py`, omit the client secret entirely because it is unnecessary for status inspection. - If a legitimate integration workflow requires access to the secret, return it only through a deliberate machine-readable interface rather than ordinary logs. - Require explicit opt-in, such as `--show-client-secret`, and display a warning before revealing it. - Ensure logging, exception handling, telemetry, and agent output redact fields named `client_secret`, `Authorization`, and similar credentials. - Review retained execution logs and remove exposed secrets where practical. Invalidate affected sessions if exposure is suspected.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/create_payment.py:31
Finding
User Guidance Encourages API Key Disclosure in Agent Conversations## Vulnerability Details **File Location**: `scripts/create_payment.py`, line 31 **Vulnerability Type**: Unsafe secret-handling guidance **Risk Level**: Medium ### Vulnerable Code ```python print("\n或直接在对话中告诉我你的 API Key,我来帮你设置") ``` The message tells the user that they may provide the API key directly in the conversation so that the agent can configure it. ### Technical Analysis A merchant API key is a sensitive credential and should not be pasted into an agent conversation. Conversation content may be retained in chat history, application logs, debugging telemetry, moderation systems, or support exports. It may also be visible to other users with access to the conversation. The declared functionality only requires the credential to be configured in the local `WOOSHPAY_API_KEY` environment variable. Asking the user to disclose it through a conversation unnecessarily expands the credential's exposure surface and violates least-disclosure practices. ### Attack Path 1. The user launches `create_payment.py` without setting `WOOSHPAY_API_KEY`. 2. The script displays guidance stating that the key can be provided directly in the conversation. 3. The user pastes the merchant API key into an agent or support chat. 4. The conversation is stored or included in logs and telemetry. 5. A person or system with access to those records obtains the key. 6. The exposed key is reused against WooshPay according to its assigned privileges. ### Impact Assessment Exposure may grant unauthorized merchant-level API access. Based on the operations implemented by this project, a sufficiently privileged key may permit payment creation, transaction lookup, checkout creation, and refund requests. The issue does not automatically transmit the key by itself; exploitation requires the user to follow the unsafe guidance. However, the instruction explicitly promotes that behavior and creates a credible credential-compromise path.
Remediation
## Remediation Suggestions - Remove the instruction inviting users to provide API keys through a conversation. - Replace it with guidance that explicitly warns users never to paste credentials into chat, command history, source code, or logs. - Recommend local configuration through a protected environment variable or operating-system secret manager. - Prefer a restricted-permission configuration file or secret store with owner-only access where environment variables are unsuitable. - Provide safe guidance such as: ```python print("Set WOOSHPAY_API_KEY locally using a secure secret manager.") print("Do not paste API keys into chat, source code, or logs.") ``` - If a key has already been disclosed in a conversation, revoke and rotate it, then review relevant transaction activity. - Use separate, least-privilege API credentials for read-only lookup and financially sensitive operations such as refunds where supported.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (27)

Tainted flow: 'headers' from os.environ.get (line 142, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
print("\n⏳ 正在创建收银台...")
    
    try:
        response = requests.post(BASE_URL, headers=headers, json=data, timeout=30)
        result = response.json()
        
        if response.status_code in [200, 201]:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'headers' from os.environ.get (line 105, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
print("\n⏳ 正在创建订单...")
    
    try:
        response = requests.post(BASE_URL, headers=headers, json=data, timeout=30)
        result = response.json()
        
        if response.status_code in [200, 201]:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'headers' from os.environ.get (line 62, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
print(f"\n⏳ 正在查询订单 {order_id}...")
    
    try:
        response = requests.get(url, headers=headers, timeout=30)
        
        if response.status_code == 200:
            result = response.json()
Confidence
99% confidence
Finding
The WooshPay API key is read from the environment and placed directly into the Authorization header for every request, including attacker-controlled URLs when full URLs are accepted. This can leak the credential to arbitrary external services, enabling unauthorized API access, payment data exposure, and potentially fraudulent operations depending on the key's privileges.

Tainted flow: 'headers' from os.environ.get (line 116, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
print("\n⏳ 正在发起退款...")
    
    try:
        response = requests.post(BASE_URL, headers=headers, json=data, timeout=30)
        result = response.json()
        
        if response.status_code in [200, 201]:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The supplied code only implements order/payment intent lookup functionality. It reads WOOSHPAY_API_KEY from the environment, prompts for an order ID, performs a GET request to the WooshPay payment_intents endpoint, and displays status/details. This aligns with the declared 'Query Order Status' scenario, but the broader declared description claims additional capabilities—creating payments, creating hosted checkout sessions, processing refunds, and managing checkout sessions—that are not present in this code chunk. There is no evidence of unrelated or dangerous undeclared behavior, but the declared purpose overstates the implemented functionality for this chunk, so this is a description/behavior mismatch.

Missing User Warnings

High
Confidence
98% confidence
Finding
The script explicitly encourages the user to reveal their API key in conversation, which creates a direct path for credential disclosure outside secure secret-handling channels. In an agent skill context, this is especially dangerous because users may assume the assistant can safely store or configure the secret, leading to compromise of payment API credentials.

Ssd 3

High
Confidence
99% confidence
Finding
Telling users to disclose the API key so it can be handled on their behalf is a credential-handling anti-pattern that can cause immediate secret leakage. Because this skill processes payment operations, exposure of the WooshPay API key could enable unauthorized payment creation, refund abuse, transaction inspection, or broader account compromise depending on key scope.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
Accepting arbitrary full URLs in an order lookup feature broadens a simple status query into an authenticated arbitrary outbound request primitive. In this payment-gateway context, that is especially dangerous because it can exfiltrate WooshPay credentials and turn a benign support/admin workflow into a credential-leak vector.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The skill is described as an order-status query utility, but this code expands that narrow function into arbitrary outbound requests while carrying WooshPay credentials. That mismatch increases risk because operators may trust the tool as a limited gateway helper while it actually enables broader network interaction and secret disclosure.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill documents use of environment secrets and outbound network access to a payment API, but it does not declare any explicit tool scope or permissions. In an agent environment, missing scope declarations can cause overbroad execution privileges, making it easier for the skill or adjacent code to access secrets or perform network actions without clear policy enforcement.

Tainted flow: 'data' from input (line 147, user input) → requests.post (network output)

Medium
Category
Data Flow
Content
print("\n⏳ 正在创建收银台...")
    
    try:
        response = requests.post(BASE_URL, headers=headers, json=data, timeout=30)
        result = response.json()
        
        if response.status_code in [200, 201]:
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The script prints the checkout session client_secret directly to stdout, exposing a sensitive secret that may be captured in terminal history, logs, screen recordings, or shared transcripts. In a payment workflow, unnecessary disclosure of session secrets increases the risk of unauthorized session use or leakage into less-trusted environments.

Natural-Language Policy Violations

Medium
Confidence
86% confidence
Finding
All prompts, instructions, and user-facing messages in the script are presented only in Chinese. This can violate language/locale policy when the skill does not offer user opt-in for Chinese or explain that the tool is intentionally region- or language-specific.

Tainted flow: 'url' from input (line 70, user input) → requests.get (network output)

Medium
Category
Data Flow
Content
print(f"\n⏳ 正在查询订单 {order_id}...")
    
    try:
        response = requests.get(url, headers=headers, timeout=30)
        
        if response.status_code == 200:
            result = response.json()
Confidence
98% confidence
Finding
The script allows the user to supply a full URL instead of only a WooshPay payment intent ID, then sends an authenticated HTTP request to that URL. Because the Authorization header contains the WooshPay API key, this creates a server-side request forgery / credential exfiltration path where an attacker can cause the script to send secrets to an arbitrary host.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script prints sensitive payment fields such as client_secret and payment method identifiers directly to the terminal. These values may be captured in shell history, terminal logs, screen recordings, support transcripts, or shared consoles, creating unnecessary exposure of confidential payment data.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The script's user-facing docstrings, prompts, warnings, and status messages are written entirely in Chinese, which imposes a specific language on all users. Under the policy rule, this is a natural-language locale constraint and there is no opt-in, fallback, or documentation indicating the skill is intentionally region-specific.

External Transmission

Medium
Category
Data Exfiltration
Content
import json
import requests

BASE_URL = "https://api.wooshpay.com/v1/refunds"

# 退款原因
REFUND_REASONS = {
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import json
import requests

BASE_URL = "https://api.wooshpay.com/v1/refunds"

# 退款原因
REFUND_REASONS = {
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import json
import requests

BASE_URL = "https://api.wooshpay.com/v1/refunds"

# 退款原因
REFUND_REASONS = {
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import json
import requests

BASE_URL = "https://api.wooshpay.com/v1/refunds"

# 退款原因
REFUND_REASONS = {
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print("\n⏳ 正在发起退款...")
    
    try:
        response = requests.post(BASE_URL, headers=headers, json=data, timeout=30)
        result = response.json()
        
        if response.status_code in [200, 201]:
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print("\n⏳ 正在发起退款...")
    
    try:
        response = requests.post(BASE_URL, headers=headers, json=data, timeout=30)
        result = response.json()
        
        if response.status_code in [200, 201]:
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print("\n⏳ 正在发起退款...")
    
    try:
        response = requests.post(BASE_URL, headers=headers, json=data, timeout=30)
        result = response.json()
        
        if response.status_code in [200, 201]:
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Description-Behavior Mismatch

Low
Confidence
94% confidence
Finding
The documentation omits an order-cancellation capability while later exposing a cancel-payment operation. Undocumented transaction-affecting actions reduce operator awareness and review quality, which can lead to misuse or unexpected cancellation of payment orders in production.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
All user-facing prompts, status messages, and the module description are written in Chinese, which imposes a specific language on users. The file does not offer any language selection or indicate that the skill is intentionally limited to Chinese-speaking users or a region-specific deployment.

Static analysis

No suspicious patterns detected.