Back to skill

Security audit

Send USD Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill claims to send real USD, but its code returns successful payment confirmations without authentication or any actual payment backend.

Do not install this as a real payment skill. It may be acceptable only as a clearly isolated mock after relabeling, but production use would need authenticated sender identity, explicit user confirmation, real provider or ledger integration, authoritative settlement status, durable audit records, and stricter amount validation.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
code.js:16
Finding
Missing Authentication and Sender Authorization<![CDATA[ ## Vulnerability Details **File Location**: `code.js`, lines 16-71 **Vulnerability Type**: Missing authentication and authorization **Risk Level**: High ### Vulnerable Code ```js export async function execute(ctx) { const { from_agent, to_agent, amount = 1.0, memo = "" } = ctx.params; // Validate inputs if (!from_agent || typeof from_agent !== "string") { return { success: false, transaction_id: null, message: "Invalid from_agent: must be a non-empty string", error_code: "INVALID_SENDER", }; } if (!to_agent || typeof to_agent !== "string") { return { success: false, transaction_id: null, message: "Invalid to_agent: must be a non-empty string", error_code: "INVALID_RECIPIENT", }; } if (typeof amount !== "number" || amount < 0.01) { return { success: false, transaction_id: null, message: "Invalid amount: must be at least $0.01", error_code: "INVALID_AMOUNT", }; } if (from_agent === to_agent) { return { success: false, transaction_id: null, message: "Cannot transfer to the same agent", error_code: "INVALID_RECIPIENT", }; } try { // Generate transaction ID const transaction_id = `txn_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`; // TODO: Integrate with your payment provider here // Example: const result = await paymentAPI.transferUSD(from_agent, to_agent, amount); // Simulated successful transfer const result = { success: true, transaction_id, amount, from_agent, to_agent, memo, timestamp: new Date().toISOString(), message: `Successfully transferred $${amount.toFixed(2)} USD from ${from_agent} to ${to_agent}`, }; return result; ``` ### Technical Analysis The sender identity is read directly from the caller-controlled `ctx.params.from_agent` field. The function only verifies that this value is a non-empt ...[truncated 2085 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Authenticate every caller before processing a transfer. 2. Derive the sender identifier from a trusted authenticated context, such as `ctx.identity.agent_id`; do not accept authoritative sender identity from request parameters. 3. If `from_agent` remains part of the request, compare it against the authenticated identity and reject any mismatch. 4. Add an explicit authorization check immediately before payment-provider invocation. 5. Use narrowly scoped payment credentials and enforce account-level permissions at the provider. 6. Record the authenticated principal, authorization decision, destination, amount, provider reference, and final provider status in an append-only audit log. 7. Add tests proving that anonymous callers and callers attempting to use another agent's identifier are rejected. 8. Keep authentication and authorization enforcement server-side; caller-provided assertions or UI restrictions are insufficient. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
code.js:54
Finding
Fabricated Successful Transfers Without Payment Execution or Verification<![CDATA[ ## Vulnerability Details **File Location**: `code.js`, lines 54-71 **Vulnerability Type**: Fail-open business logic and false payment confirmation **Risk Level**: High ### Vulnerable Code ```js try { // Generate transaction ID const transaction_id = `txn_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`; // TODO: Integrate with your payment provider here // Example: const result = await paymentAPI.transferUSD(from_agent, to_agent, amount); // Simulated successful transfer const result = { success: true, transaction_id, amount, from_agent, to_agent, memo, timestamp: new Date().toISOString(), message: `Successfully transferred $${amount.toFixed(2)} USD from ${from_agent} to ${to_agent}`, }; return result; ``` ### Technical Analysis The function never invokes a payment provider, checks a balance, reserves funds, performs settlement, or persists a transaction. Nevertheless, it unconditionally creates a local identifier and returns `success: true` for every request that passes basic validation. The identifier is not a provider-issued transaction reference and is not evidence of settlement. The behavior also contradicts the skill documentation, which describes actual USD transfers, insufficient-funds failures, transaction logging, authentication, and possible transfer limits. This is a fail-open financial workflow: the absence of payment execution is represented as success rather than as an unavailable or unimplemented operation. Any downstream component that trusts the result may release goods, grant service, update a balance, or mark an invoice as paid without receiving funds. ### Attack Path 1. An attacker submits syntactically accepted sender, recipient, and amount values. 2. The request passes the basic validation conditions. 3. The function generates a plausible-looking local transaction identifier. 4. No payment API or settlement system is contacted. 5. The function returns `success ...[truncated 846 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Until payment integration exists, return a failure with an explicit status such as `NOT_IMPLEMENTED`; never return payment success from a simulation in production code. 2. Return `success: true` only after receiving and validating an authoritative success or settlement response from the payment provider. 3. Use a provider-issued transaction reference rather than a locally generated identifier as evidence of the external operation. 4. Distinguish states such as `created`, `pending`, `authorized`, `settled`, `failed`, and `reversed`; do not equate request acceptance with settlement. 5. Persist transaction attempts and provider responses in a durable, tamper-resistant audit store. 6. Implement balance checks, idempotency keys, replay protection, rate limits, and daily transfer limits. 7. Cryptographically verify provider webhooks and reconcile asynchronous settlement before granting irreversible value. 8. Update the documentation so its guarantees accurately match the implemented behavior. 9. Add integration tests confirming that provider errors, timeouts, and unavailable services never produce successful transfer responses. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
code.js:36
Finding
Non-Finite and Unbounded Monetary Amounts Are Accepted<![CDATA[ ## Vulnerability Details **File Location**: `code.js`, lines 36-68 **Vulnerability Type**: Improper validation of financial values **Risk Level**: Medium ### Vulnerable Code ```js if (typeof amount !== "number" || amount < 0.01) { return { success: false, transaction_id: null, message: "Invalid amount: must be at least $0.01", error_code: "INVALID_AMOUNT", }; } ``` The accepted value is subsequently included in a successful result: ```js const result = { success: true, transaction_id, amount, from_agent, to_agent, memo, timestamp: new Date().toISOString(), message: `Successfully transferred $${amount.toFixed(2)} USD from ${from_agent} to ${to_agent}`, }; ``` ### Technical Analysis The validation checks only the JavaScript type and lower bound. In JavaScript, `NaN` and positive `Infinity` both have the type `"number"`. Furthermore: - `NaN < 0.01` evaluates to `false`. - `Infinity < 0.01` evaluates to `false`. Both values therefore bypass the validation and are represented as successful transfer amounts. The implementation also has no maximum transfer amount and does not enforce a two-decimal USD precision policy. Using binary floating-point numbers directly for currency can additionally introduce rounding inconsistencies. Monetary values should normally be represented as integer minor units, such as cents, or by a validated decimal type. ### Attack Path 1. An attacker invokes the JavaScript function directly or through an input-binding layer capable of producing non-finite numeric values. 2. The attacker supplies `NaN` or positive `Infinity` as `amount`. 3. `typeof amount === "number"` evaluates to true. 4. The lower-bound comparison evaluates to false, so the rejection branch is skipped. 5. The function returns a successful result containing an invalid financial amount and a malformed success message. 6. A downstream ledger, serializer, logger, analytics process, or future payment-provider adapter may reje ...[truncated 796 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject non-finite values explicitly with `Number.isFinite(amount)`. 2. Enforce both minimum and maximum transaction values according to a documented policy. 3. Represent USD as integer cents rather than binary floating-point dollars. 4. If decimal input is accepted, validate its textual format before converting it and reject values with more than two decimal places. 5. Apply equivalent validation at every trust boundary, including the API schema, application layer, ledger, and payment-provider adapter. 6. Reject negative zero, unsafe integers, overflow conditions, and values outside the provider's supported range. 7. Add tests for `NaN`, `Infinity`, `-Infinity`, excessively large values, excessive decimal precision, and boundary values. Example hardened validation using integer cents: ```js if ( !Number.isSafeInteger(amount_cents) || amount_cents < 1 || amount_cents > MAX_TRANSFER_CENTS ) { return { success: false, transaction_id: null, message: "Invalid amount", error_code: "INVALID_AMOUNT", }; } ``` ]]>
Vulnerability Patterns
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This skill performs irreversible financial actions but does not warn the user or require an explicit confirmation step before initiating a transfer. In agent-driven environments, ambiguous or automatic execution of payment actions can lead to unintended fund transfers from prompt injection, user misunderstanding, or misrouted agent requests.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The skill is described as sending USD between agents, but the implementation only fabricates a success response and transaction ID without invoking any payment rail or ledger update. This can mislead calling systems into believing funds were transferred, causing accounting inconsistencies, false settlement, business logic bypass, or downstream release of goods/services based on a non-existent payment.

Static analysis

No suspicious patterns detected.