Back to skill

Security audit

Billing

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent billing-integration guidance skill, but it includes copyable high-impact payment examples with insufficient guardrails and a few unsafe financial implementation patterns.

Review the examples before using this skill in production billing work. Use Stripe/Paddle test mode first, require explicit approval before actions that charge, refund, send invoices, alter subscriptions, or update connected accounts, and replace the unsafe webhook, refund, and floating-point billing examples with durable, audited, integer-money implementations.

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

T09 · Insecure Skill Coding Practices

Error
Location
webhooks.md:96
Finding
Webhook Events Are Acknowledged Before Durable Processing<![CDATA[ ## Vulnerability Details **File Location**: `webhooks.md:96-100` **Vulnerability Type**: Premature webhook acknowledgment and unreliable asynchronous processing **Risk Level**: High ### Vulnerable Code ```typescript // Acknowledge immediately res.status(200).json({ received: true }); // Process async (prevents timeout) processEventAsync(event); ``` ### Technical Analysis The endpoint returns HTTP 200 before the verified event has been processed or placed in a durable queue. Payment service providers interpret the successful response as confirmation that the event was accepted and generally stop retrying it. The asynchronous operation is neither awaited nor shown as being persisted to a durable job queue. If the application terminates, scales down, loses connectivity, or encounters an unhandled rejection after returning the response, the event can be permanently lost. Webhook idempotency does not address this failure mode because there will be no retry after the provider receives HTTP 200. ### Attack Path 1. An attacker with access to a legitimate customer account triggers a billing event at a strategically chosen time, or a normal payment event occurs. 2. The webhook endpoint verifies the event and immediately returns HTTP 200. 3. The application process is interrupted, restarted, or otherwise fails before `processEventAsync(event)` completes. 4. The payment provider records the webhook as delivered and does not retry it. 5. The corresponding subscription, payment, cancellation, or dispute state is omitted from the application database. An attacker cannot forge a Stripe event if signature verification is correctly implemented, but they may exploit operational instability by repeatedly causing legitimate events. The same data-loss condition can also occur without malicious involvement. ### Impact Assessment No direct operating-system privileges are obtained. The affected scope is the application's billing and authorization state. Lost events ...[truncated 292 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Verify the webhook signature and then persist the event to a durable database table or message queue before returning HTTP 200. - Acknowledge the request only after the durable enqueue or transaction succeeds. - Use a unique constraint on the provider event ID to preserve idempotency. - Process persisted events in a monitored worker with bounded retries and a dead-letter queue. - Record explicit states such as `received`, `processing`, `completed`, and `failed`. - Add reconciliation jobs that compare local billing state with the payment provider. - Monitor queue lag, processing failures, and events that remain incomplete beyond a defined threshold. - If no durable queue is available, await processing before returning and allow the provider to retry on failure. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
webhooks.md:102
Finding
Webhook Endpoint Reflects Internal Exception Messages<![CDATA[ ## Vulnerability Details **File Location**: `webhooks.md:102-104` **Vulnerability Type**: Information disclosure through verbose error responses **Risk Level**: Low ### Vulnerable Code ```typescript } catch (err) { // Stripe will retry on 4xx/5xx res.status(400).send(`Webhook Error: ${err.message}`); } ``` ### Technical Analysis The endpoint sends the raw exception message to an unauthenticated remote caller. Exception messages may reveal signature-validation behavior, framework details, data formats, internal identifiers, or library implementation information. Because webhook routes are normally internet-accessible, an attacker can submit malformed requests and compare responses to enumerate validation paths. Although this does not directly bypass signature verification, the disclosed information may assist endpoint reconnaissance and make subsequent attacks more efficient. ### Attack Path 1. An attacker sends malformed requests to the public webhook endpoint. 2. Each request is varied by changing headers, body encoding, content type, or signature format. 3. The endpoint catches the resulting exceptions. 4. The application reflects each raw `err.message` in the HTTP response. 5. The attacker compares the messages to infer implementation details and validation behavior. ### Impact Assessment This issue does not directly grant application or system privileges. Its primary impact is information disclosure to unauthenticated users. Depending on the underlying exception content, the exposed scope could include: - Payment SDK or framework behavior. - Signature-validation details. - Internal object identifiers. - Request parsing assumptions. - Operational details useful for targeted denial-of-service or validation-bypass research. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Return a generic response such as `Invalid webhook request` without including the exception message. - Log detailed exceptions only on the server. - Redact secrets, signatures, payment data, personal information, and raw request bodies from logs. - Restrict production log access according to least privilege. - Assign a non-sensitive correlation ID to each failed request and include only that ID in the response. - Apply rate limiting and monitoring to repeated invalid webhook requests. - Ensure error handling distinguishes invalid signatures from temporary internal failures so retry behavior remains appropriate. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
marketplace.md:98
Finding
Marketplace Split-Refund Example Returns Only Half of the Requested Refund<![CDATA[ ## Vulnerability Details **File Location**: `marketplace.md:98-104` **Vulnerability Type**: Incorrect marketplace refund accounting **Risk Level**: High ### Vulnerable Code ```typescript // Option 3: Split await stripe.refunds.create({ charge: chargeId, amount: amount * 0.5, // Customer gets full refund reverse_transfer: true }); // Platform eats the other half ``` ### Technical Analysis The code and comments conflict. The Stripe refund amount is explicitly set to `amount * 0.5`, so only half of the requested amount is returned to the customer. The `reverse_transfer` option controls whether funds are recovered from the connected account; it does not cause the platform to fund and issue the omitted half of the customer refund. Consequently, an implementation copied from this example could record or communicate a full refund while Stripe processes only a partial refund. The multiplication also introduces a secondary precision concern if `amount` is not guaranteed to produce an integer result, because Stripe expects amounts in the currency's smallest unit. ### Attack Path 1. A marketplace order is selected for a full refund. 2. The application chooses the documented split option to divide liability between the vendor and platform. 3. The code submits only `amount * 0.5` as the refund amount. 4. Stripe returns only half of the intended amount to the customer. 5. Application logic or staff may treat the operation as a completed full refund because the accompanying comment states that the customer receives the full amount. 6. The customer remains under-refunded while marketplace accounting diverges from the actual provider transaction. ### Impact Assessment No technical privileges are obtained, but the issue affects customer funds and marketplace accounting. Potential consequences include: - Customers receiving only partial refunds. - Incorrect vendor and platform balance allocation. - Increased disputes and chargebacks. - Consumer-protectio ...[truncated 152 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Submit the full intended customer refund amount to Stripe. - Implement platform-versus-vendor liability separately using supported transfer reversal and balance accounting operations. - Validate that refund amounts are positive integers in the currency's smallest unit. - Retrieve and verify the final provider refund object before marking an order as fully refunded. - Track customer refund amount, vendor recovery amount, and platform absorption amount as separate ledger fields. - Add integration tests that assert the customer's total refunded amount equals the approved refund. - Make refund processing idempotent to prevent duplicate refunds during retries. - Require authorization and ownership checks before accepting `chargeId` or refund amounts from application callers. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
usage-billing.md:110
Finding
Usage Billing Performs Monetary Calculations with Floating-Point Values<![CDATA[ ## Vulnerability Details **File Location**: `usage-billing.md:110-117` **Vulnerability Type**: Unsafe floating-point monetary calculation **Risk Level**: Medium ### Vulnerable Code ```typescript // Soft limit with overage const included = 10000; // API calls const overageRate = 0.001; // $0.001 per call over function calculateBill(usage: number) { const baseFee = 49.00; const overage = Math.max(0, usage - included) * overageRate; return baseFee + overage; } ``` ### Technical Analysis The example calculates currency using JavaScript `number` values expressed in dollars. JavaScript uses binary IEEE-754 floating-point arithmetic, and many decimal values cannot be represented exactly. Repeated calculations, aggregation, currency conversion, or inconsistent rounding can therefore produce invoice amounts that differ from expected values. This code also contradicts the project's core rule in `SKILL.md` that monetary amounts must be represented as integers in the currency's smallest unit. The overage rate of `$0.001` is smaller than one cent. Correct handling therefore requires a clearly defined precision and rounding policy rather than ordinary floating-point arithmetic. ### Attack Path 1. A customer generates a usage quantity that produces a decimal amount that cannot be represented exactly in binary floating point. 2. The application calculates overage and total charges using JavaScript `number`. 3. Different components round the result at different stages, such as dashboard aggregation, invoice generation, database storage, or PSP submission. 4. The customer is overcharged or undercharged, or the provider rejects a non-integer smallest-unit amount. 5. Repeating this across many events or customers amplifies the reconciliation difference. A customer who can intentionally control usage quantities may select boundary values that maximize favorable rounding, although the magnitude depends on the final rounding implementation. ### Impact Assess ...[truncated 377 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Represent ordinary currency amounts as integers in the currency's smallest unit. - Where rates require sub-cent precision, use a fixed-point decimal library or integer micro-units. - Define when sub-cent totals are rounded to billable currency units. - Apply one documented rounding mode consistently, such as half-up or banker's rounding, according to business and legal requirements. - Convert to provider-compatible integer units only at a controlled finalization boundary. - Store the original usage, rate, exact calculated amount, rounding adjustment, and final billed amount for auditability. - Add tests for boundary quantities, large usage values, zero usage, negative input, and cumulative rounding across many events. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (6)

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
Line L102 contains instructional content in Spanish while the rest of the skill is in English. This introduces a language/locale inconsistency without any user opt-in or documented reason, which can violate language policy expectations and reduce accessibility for users expecting a single language.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
### Operational Errors
- Sending payment reminders during contractual grace period
- Dunning without checking for open disputes → double loss
- Proration without specifying mode → unexpected customer charges
- Refunding without checking for existing chargeback → paying twice
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
### Operational Errors
- Sending payment reminders during contractual grace period
- Dunning without checking for open disputes → double loss
- Proration without specifying mode → unexpected customer charges
- Refunding without checking for existing chargeback → paying twice
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This markdown file includes example onboarding code that updates an account with personally sensitive information such as `ssn_last_4` and even notes that a full SSN may be collected. The surrounding documentation does not warn readers about privacy, secure handling, or compliance obligations when collecting this data.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This markdown file provides copyable Stripe examples that create checkout sessions, confirm off-session payment intents, create subscriptions, apply coupons, and finalize/send invoices, all of which can affect customer billing and account state. The document does not include any user-facing warning that these actions may create charges, alter subscriptions, or send invoices if run against live data.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
In this markdown file, the embedded code example performs a network request to an external government API and includes the VAT number in the request body. The surrounding documentation does not warn that entered VAT identifiers and related data will be transmitted off-system, which is relevant to privacy and compliance expectations.

Static analysis

No suspicious patterns detected.