Back to skill

Security audit

Payment Gateway Toolkit

Security checks for vulnerabilities and agentic risk

Overview

This payment skill is coherent but needs Review because it can create payments and refunds while missing important safeguards around live financial actions, credentials, and sensitive payment data.

Install only for sandbox or carefully controlled payment-development use. Do not use production Stripe or Alipay credentials until refund flows require explicit approval, monetary values are validated with Decimal or integer minor units, secrets are redacted from logs/history, dependencies are pinned, and network/API access is scoped to the intended payment accounts.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/payment_handler.py:33
Finding

Process-Global Stripe API Key Causes Cross-Instance Credential Confusion

Content
View full analysis

Vulnerability Details

File Location: scripts/payment_handler.py:33-39, with global-key API use at scripts/payment_handler.py:77-83 and scripts/payment_handler.py:205-210
Vulnerability Type: Shared global credential state
Risk Level: High

Vulnerable Code

python
# Initialize Stripe
if stripe_key:
    stripe.api_key = stripe_key
    self.stripe_enabled = True
else:
    self.stripe_enabled = False

Stripe operations are subsequently performed without passing an instance-specific key:

python
intent = stripe.PaymentIntent.create(
    amount=int(amount * 100),
    currency=currency.lower(),
    description=description,
    metadata=metadata or {}
)
python
refund_data = {"payment_intent": payment_intent_id}
if amount:
    refund_data["amount"] = int(amount * 100)

refund = stripe.Refund.create(**refund_data)

Technical Analysis

Each PaymentHandler stores self.stripe_key, but initialization also assigns that credential to the process-global stripe.api_key. Stripe requests do not use self.stripe_key; they use whichever credential was most recently assigned globally.

In an application with multiple merchants, tenants, tests, background jobs, or concurrently initialized handlers, constructing one handler silently changes the credentials used by every other handler in the process. A handler that appears to represent merchant A can therefore create payments or attempt refunds through merchant B's Stripe account.

This violates tenant isolation and can produce credential races under concurrent execution.

Attack Path

  1. A service creates PaymentHandler(stripe_key=key_for_merchant_a).
  2. Another tenant or worker creates PaymentHandler(stripe_key=key_for_merchant_b).
  3. The second initialization overwrites the global stripe.api_key.
  4. The first handler calls create_stripe_order() or refund_stripe_order().
  5. Stripe receives the ...[truncated 740 chars]
Remediation
View remediation

Remediation Suggestions

  • Do not assign tenant credentials to stripe.api_key.

  • Use a request-scoped Stripe client or pass the API key explicitly for every request. With supported Stripe SDK versions, prefer StripeClient:

    python
    from stripe import StripeClient
    
    self.stripe_client = StripeClient(stripe_key)
    intent = self.stripe_client.v1.payment_intents.create({...})
    
  • If compatibility requires static resource methods, pass api_key=self.stripe_key on every call rather than relying on global state.

  • Validate that each payment or refund belongs to the authenticated merchant before issuing an operation.

  • Add concurrent and multi-tenant tests that initialize handlers with different keys and assert that each outgoing request uses the correct credential.

  • Use restricted Stripe keys containing only the permissions required by this toolkit.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/payment_handler.py:196
Finding

Zero-Valued Partial Refund Is Silently Converted into a Full Refund

Content
View full analysis

Vulnerability Details

File Location: scripts/payment_handler.py:196-210
Vulnerability Type: Financial transaction validation error
Risk Level: High

Vulnerable Code

python
def refund_stripe_order(self, payment_intent_id: str, amount: Optional[float] = None) -> Dict[str, Any]:
    """
    发起Stripe退款

    Args:
        payment_intent_id: 支付意图ID
        amount: 退款金额,不传则全额退款

    Returns:
        退款结果
    """
    if not self.stripe_enabled:
        raise ValueError("Stripe not initialized.")

    try:
        refund_data = {"payment_intent": payment_intent_id}
        if amount:
            refund_data["amount"] = int(amount * 100)

        refund = stripe.Refund.create(**refund_data)

Technical Analysis

The code uses a truthiness check instead of checking whether the optional amount is None. Python treats 0 and 0.0 as false. Consequently, a caller that explicitly requests a zero-valued refund causes the amount field to be omitted from the Stripe request.

Stripe interprets a refund request without an amount as a request to refund the remaining payment balance. A zero-value request can therefore become a full refund. The function also lacks an explicit positive-amount validation boundary.

Attack Path

  1. An application exposes refund functionality through this method and permits an operator, integration, or user-controlled workflow to submit a refund amount.
  2. The caller supplies amount=0 or amount=0.0, possibly expecting validation failure or a no-op.
  3. if amount evaluates to false, so the amount is omitted.
  4. stripe.Refund.create(payment_intent=...) is issued.
  5. Stripe processes the request as a full refund of the remaining refundable balance.

Impact Assessment

An actor who is already able to initiate refunds could escalate an intended zero-value or validation-only operation into a full refund. The impact is direct financial loss for t ...[truncated 195 chars]

Remediation
View remediation

Remediation Suggestions

  • Distinguish an omitted amount from a zero amount:

    python
    if amount is not None:
        refund_amount = Decimal(str(amount))
        if refund_amount <= 0:
            raise ValueError("Refund amount must be greater than zero")
        refund_data["amount"] = to_minor_units(refund_amount, currency)
    
  • Reject zero, negative, non-finite, and excessively precise monetary values before contacting Stripe.

  • Require explicit authorization for full refunds rather than representing them solely through an omitted argument.

  • Retrieve the original payment and verify the requested refund does not exceed its remaining refundable amount.

  • Add tests for None, 0, negative values, fractional values, and amounts greater than the payment balance.

  • Use idempotency keys for refund requests to prevent duplicate financial operations during retries.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/payment_handler.py:66
Finding

Floating-Point Truncation Can Alter Payment and Refund Amounts

Content
View full analysis

Vulnerability Details

File Location: scripts/payment_handler.py:66-80 and scripts/payment_handler.py:196-210
Vulnerability Type: Insecure monetary amount conversion
Risk Level: Medium

Vulnerable Code

python
def create_stripe_order(self,
                       amount: float,
                       currency: str = "usd",
                       description: str = "",
                       metadata: Optional[Dict] = None) -> Dict[str, Any]:
    if not self.stripe_enabled:
        raise ValueError("Stripe not initialized. Please provide stripe_key.")

    try:
        intent = stripe.PaymentIntent.create(
            amount=int(amount * 100),  # Stripe使用最小货币单位
            currency=currency.lower(),
            description=description,
            metadata=metadata or {}
        )

The same conversion is used for partial refunds:

python
refund_data = {"payment_intent": payment_intent_id}
if amount:
    refund_data["amount"] = int(amount * 100)

refund = stripe.Refund.create(**refund_data)

Technical Analysis

Monetary values are represented as binary floating-point numbers and converted using int(amount * 100). Binary floating-point cannot represent many decimal currency values exactly, while int() truncates rather than applying an explicit currency rounding rule. This can produce a minor-unit amount different from the displayed or recorded amount.

The conversion also assumes every currency has exactly two decimal places. Stripe supports currencies with zero-decimal and other special minor-unit behavior. No validation rejects negative, non-finite, over-precision, or unsupported amounts.

The returned order history records the original floating-point amount, not necessarily the exact integer sent to Stripe, which can create accounting and fulfillment discrepancies.

Attack Path

  1. A caller supplies an amount whose floating-point representation fall ...[truncated 1126 chars]
Remediation
View remediation

Remediation Suggestions

  • Accept monetary values as Decimal or integer minor units, not float.
  • Parse external decimal strings with Decimal(str(value)).
  • Maintain an authoritative currency-to-minor-unit mapping and reject unsupported currencies.
  • Apply an explicit rounding policy, such as ROUND_HALF_UP, and reject values with excess precision where appropriate.
  • Validate that amounts are finite, strictly positive, and within provider limits.
  • Record the exact integer minor-unit amount sent to Stripe and use Stripe's response as the authoritative transaction record.
  • Add test vectors for floating-point edge cases and zero-decimal currencies.

T09 · Insecure Skill Coding Practices

Warning
Location
examples/basic_usage.py:24
Finding

Stripe Payment Client Secret Is Retained and Printed to Standard Output

Content
View full analysis

Vulnerability Details

File Location: scripts/payment_handler.py:84-97 and examples/basic_usage.py:24-32
Vulnerability Type: Sensitive payment token exposure
Risk Level: Medium

Vulnerable Code

The complete payment-intent client secret is placed into the returned object and retained in order history:

python
order_info = {
    "provider": "stripe",
    "order_id": intent.id,
    "client_secret": intent.client_secret,
    "amount": amount,
    "currency": currency,
    "status": intent.status,
    "created_at": datetime.now().isoformat(),
    "description": description
}

self.order_history.append(order_info)
return order_info

The example prints the entire object:

python
order = handler.create_stripe_order(
    amount=99.99,
    currency="usd",
    description="Premium Subscription",
    metadata={"customer_id": "cust_123", "plan": "premium"}
)

print(f"订单创建成功: {order}")

# 模拟前端使用client_secret完成支付...

Technical Analysis

Stripe payment-intent client secrets are bearer-like payment tokens intended only for the customer session that completes the payment. They should not be logged, broadly retained, placed in URLs, or exposed to unrelated users.

The method stores the secret in the general-purpose order_history list and returns it as part of a broadly printable dictionary. The bundled example explicitly prints that dictionary, encouraging users to write the secret into terminal output, CI logs, container logs, observability systems, or support records.

Because get_order_history() returns stored order dictionaries, any code with access to the handler's order-history interface also receives all retained client secrets.

Attack Path

  1. The application creates a Stripe payment intent through the toolkit.
  2. The client secret is stored in order_history and included in the returned dictionary.
  3. Code following the bundled example prints or l ...[truncated 835 chars]
Remediation
View remediation

Remediation Suggestions

  • Never print or log an object containing a Stripe client secret.
  • Return the client secret only through a narrowly scoped response to the authenticated customer who owns the payment session.
  • Store non-sensitive order history separately and omit client_secret from retained records.
  • Redact keys such as client_secret, API keys, signatures, and payment URLs in structured logging middleware.
  • Apply authorization checks that bind each payment intent to the current customer and merchant.
  • Configure short log retention and restricted log access, and rotate or recreate affected payment intents if secrets have already been exposed.
  • Add automated tests that assert secrets are absent from history and log output.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Unbounded Dependencies Make Installations Non-Reproducible and Expose Future Supply-Chain Changes

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:1-3
Vulnerability Type: Unpinned third-party dependencies
Risk Level: Low

Vulnerable Code

text
stripe>=7.0.0
alipay-sdk-python>=3.3.0
requests>=2.28.0

Technical Analysis

All dependencies use minimum-version constraints without an upper bound, exact version, lock file, or artifact hash. Two installations performed at different times can therefore install materially different code. A newly published major version can also be selected despite incompatible API or security behavior.

No evidence in the audited files establishes that any current dependency is malicious. The risk is that future compromised releases, maintainer-account compromise, or incompatible updates can enter builds automatically without a reviewed source change.

requests is imported by the core module but not used, unnecessarily increasing the dependency set and supply-chain surface.

Attack Path

  1. A future release satisfying one of the >= constraints is published or a dependency's release process is compromised.
  2. A user or CI system installs the project without a previously reviewed lock file.
  3. The package resolver selects the new release automatically.
  4. Package installation hooks or imported runtime code execute with the privileges of the installer or application.
  5. A malicious release could access payment credentials, private keys, environment variables, files, and network resources available to that process.

This is a prospective supply-chain path; the audit found no evidence that the currently named packages or available versions are malicious.

Impact Assessment

If a selected dependency release were compromised, code could execute with the full privileges of the installation or application process and access configured Stripe and Alipay credentials. More commonly, uncontrolled major upgrades can cause availability failures or changed pa ...[truncated 161 chars]

Remediation
View remediation

Remediation Suggestions

  • Generate and commit a reviewed lock file with exact versions.
  • Use hashes for deployment dependencies, for example through pip-compile --generate-hashes and pip install --require-hashes.
  • Separate direct dependencies from resolved transitive dependencies.
  • Remove the unused requests dependency and import unless it is genuinely required.
  • Configure dependency-update automation to open reviewed pull requests rather than silently selecting new releases during deployment.
  • Run vulnerability and license scanning against the fully resolved dependency graph.
  • Test major SDK upgrades explicitly before changing pinned versions.
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 (15)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README advertises order creation, refunds, payment queries, and async callback handling without warning that these operations can trigger real financial actions or transmit payment data to external processors. In a payment skill, omission of safety guidance materially increases the risk of accidental charges, unintended refunds, and improper handling of sensitive transaction data by users following the documentation verbatim.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The quick-start code demonstrates creating Stripe and Alipay orders directly, but provides no caution to avoid production credentials or real merchant accounts. Because users commonly copy-paste examples, this can lead to immediate real-world payment initiation, accidental billing, or exposure of payment-related secrets and customer data to live gateways.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The English overview repeats payment and refund capabilities without any user-facing notice about financial consequences or privacy implications. In the context of a payment integration skill, missing warnings reduce operator awareness and make accidental transmission of order/payment metadata to third parties more likely.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill describes payment integrations that inherently require outbound network access, but the manifest does not declare any tool scope such as permissions or allowed-tools. That mismatch weakens policy enforcement and reviewability, making it easier for a consumer or agent runtime to invoke networked payment behavior without explicit authorization boundaries.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill advertises creating orders, processing refunds, and handling payment credentials, but provides no warning that these operations can move real money or expose sensitive secrets. In a payment context this omission is dangerous because users or downstream agents may run examples with live credentials, trigger unintended transactions/refunds, or mishandle private keys and webhook data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The file-level title and descriptions are written in Chinese and present the skill as Chinese-language by default, with no indication that language choice is optional. This can violate language/locale policy when a skill implicitly forces a specific language 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
89% confidence
Finding

The method returns sensitive payment/account-related fields from Alipay order queries, including buyer_logon_id and the full raw_response, without any access control, minimization, or warning. In a payment-processing context, exposing payer identifiers and unfiltered gateway responses can leak personal or financial metadata to unauthorized callers or downstream logs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This method initiates a Stripe refund, which is a potentially irreversible financial operation. The code contains no confirmation prompt, user-visible log/disclosure, or explicit warning in comments/docstrings that a refund will be executed when called.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This method triggers an Alipay refund, a safety-critical and potentially irreversible payment action. The implementation lacks any confirmation mechanism, user-facing notice, or explicit warning beyond the basic technical description.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

This Python file contains natural-language descriptions and user-facing output entirely in Chinese, including the module docstring and printed status messages. Under the policy rule for language/locale, this is a violation because the skill presents a fixed language experience without any opt-in, fallback, or explanation that the tool is intentionally region-specific.

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 version only, which makes builds non-reproducible and allows future installs to pull in unexpected major or minor releases. In a payment-processing toolkit, dependency drift can introduce breaking changes or newly introduced vulnerable versions into code that handles sensitive financial operations.

Content

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

text
stripe>=7.0.0
alipay-sdk-python>=3.3.0
requests>=2.28.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

Using an unpinned Alipay SDK version permits uncontrolled upgrades at install time, reducing supply-chain integrity and reproducibility. Because this skill is for payment gateway integration, unexpected SDK behavior or a compromised upstream release could directly affect transaction handling, refunds, or credential use.

Content

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

text
stripe>=7.0.0
alipay-sdk-python>=3.3.0
requests>=2.28.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

The requests package is unpinned, so installations may resolve to different versions over time, including releases with known security issues or incompatible behavior. In a payment-related integration that likely performs authenticated API calls, instability or vulnerable HTTP client behavior can expose credentials, request integrity, or sensitive transaction data.

Content

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

text
stripe>=7.0.0
alipay-sdk-python>=3.3.0
requests>=2.28.0

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +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
97% confidence
Finding

The manifest does not pin requests, and the package has multiple known advisories across versions, so there is no assurance that deployed environments will avoid affected releases. In a payment gateway toolkit, an HTTP client vulnerability can be especially risky because it may impact API authentication, credential handling, redirects, or leakage of sensitive financial data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The module docstring and multiple test docstrings/comments are written in Chinese, which imposes a specific language in the skill's natural-language content. Under the policy, language constraints should either offer user choice or be explicitly justified as region-specific.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.