Back to skill

Security audit

x402 Payment Protocol

Security checks for vulnerabilities and agentic risk

Overview

This is a real x402 payment helper, but it can automatically authorize USDC payments from a wallet using payment terms supplied by remote servers without enough user control.

Install only after careful review. Use a dedicated low-balance wallet, restrict calls to trusted x402 providers, add explicit user approval and maximum spend checks before any signature, validate recipient and USDC contract addresses, and do not run npm test with real wallet credentials.

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
x402.mjs:69
Finding

Automatic Signing of Untrusted, Unbounded Payment Requirements

Content
View full analysis
= 2 || String(maxAmount).length > 6) { // Already in base units value = BigInt(maxAmount); } else { // Legacy: whole token amount value = BigInt(maxAmount) * BigInt(1e6); } // Authorization struct const authorization = { from: account.address, to: requirement.payTo || requirement.payToAddress, value, validAfter, validBefore, nonce, }; // EIP-712 domain const domain = { name: requirement.extra?.name || "USD Coin", version: requirement.extra?.version || "2", chainId: network.chainId, verifyingContract: requirement.asset || requirement.usdcAddress, }; // EIP-712 types const types = { TransferWithAuthorization: [ { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" }, { name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" }, ], }; // Sign const signature = await account.signTypedData({ domain, types, primaryType: "TransferWithAuthorization", message: authorization, }); ``` ```javascript // Find supported payment option const requirement = requirements.accepts.find( r => r.scheme === "exact" && SUPPORTED_NETWORKS[r.network] ); if (!requirement) { throw new Error("No supported payment options found"); } // Create and sign payment const payment = await createPaymentSignature( account, requirement, requirements.x402Version ); // Retry with payment header const paidResponse = await fetch(url ...[truncated 2421 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
x402.mjs:69
Finding

Ambiguous Amount Parsing Can Produce Massive Overpayments

Content
View full analysis
= 2 || String(maxAmount).length > 6) { // Already in base units value = BigInt(maxAmount); } else { // Legacy: whole token amount value = BigInt(maxAmount) * BigInt(1e6); } ``` ### Technical Analysis For protocol versions below 2, integer values with six or fewer characters are treated as whole USDC and multiplied by one million. Longer values are treated as already denominated in base units. String length is not a reliable indication of monetary denomination. For example, if a server sends `"500000"` as 500,000 base units, representing 0.5 USDC, the code instead calculates: ```text 500000 × 1,000,000 = 500,000,000,000 base units ``` That authorization represents 500,000 USDC rather than 0.5 USDC. A missing `x402Version` also falls back to version 1 and enters this heuristic. The decimal branch additionally uses `parseFloat` and `Math.floor`. Binary floating-point conversion is unsuitable for security-sensitive monetary values because it can introduce precision loss, truncation, and inconsistent handling of unusual numeric strings. Because `x402Fetch` immediately signs the calculated value, the parsing error directly affects the amount authorized by the wallet. ### Attack Path 1. A server returns an HTTP 402 response with an omitted or version-1 `x402Version`. 2. The selected requirement contains `maxAmountRequired: "500000"`, intended or presented as a base-unit value. 3. The value has six characters, so the legacy branch treats it as 500,000 whole USDC. 4. The code multiplies it by `1e6`. 5. The wallet ...[truncated 660 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
package.json:7
Finding

Test Command Can Contact a Production Payment Endpoint and Trigger Signing

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (12)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 27)May include surrounding context.

md
import { x402Fetch } from './x402.mjs';

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 43)May include surrounding context.

md
import { x402Fetch } from './x402.mjs';

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 69)May include surrounding context.

md
import { x402Fetch } from './x402.mjs';

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 78)May include surrounding context.

md
### `conway-credits.mjs` — Conway Compute Credits

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The function automatically interprets a 402 response, selects a payment option, signs an EIP-712 authorization with the caller account, and retransmits it without any explicit user approval, policy check, or trust validation of the payee and asset details. This is dangerous because any endpoint that can trigger a 402 response can cause the client to produce a reusable payment authorization for USDC, potentially resulting in unintended or unauthorized payments.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explicitly demonstrates x402Fetch(account, ...) performing automatic payment handling, which can spend wallet funds as part of a normal resource fetch without any built-in confirmation, spending limit, or prominent warning at the point of use. In an agent setting, this creates a real risk of unintended or repeated on-chain payments to arbitrary endpoints if the URL or payment requirements are attacker-controlled or insufficiently vetted.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 34)May include surrounding context.

const account = privateKeyToAccount(process.env.PRIVATE_KEY);

// Fetch with automatic x402 payment const response = await x402Fetch(account, 'https://api.example.com/paid-resource'); const data = await response.json();

text

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SKILL.md (reported line 101)May include surrounding context.

md
Recommended patterns:
- Environment variables: `process.env.PRIVATE_KEY`
- Secure file with restricted permissions: `chmod 600 wallet.json`
- Hardware wallet via WalletConnect (advanced)

## Supported Networks

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The script reads a private key either directly from the PRIVATE_KEY environment variable or from a wallet file, which is a sensitive credential handling operation. Although the file header shows usage, it does not include a cautionary warning about exposing private keys or wallet files, and the code itself provides no user-facing disclosure beyond an error message when variables are missing.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · examples/conway-credits.mjs (reported line 38)May include surrounding context.

js
console.log(`Wallet: ${account.address}`);
  console.log(`Topping up $${amountUsd} Conway credits...\n`);
  
  const url = `https://api.conway.tech/pay/${amountUsd}/${account.address}`;
  
  const response = await x402Fetch(account, url);
  const result = await response.json();

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The retry request sends the signed payment authorization in the X-Payment header to the remote server with no visible disclosure, audit hook, or confirmation path. Even if the signature is intended for payment, transmitting it automatically to an untrusted endpoint increases the risk of silent value transfer or misuse of signed authorization data if the server or intermediary is malicious or compromised.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

The dependency is specified with a caret range ("^2.0.0"), which allows newer minor and patch releases to be installed. This creates supply-chain risk because future upstream releases could introduce malicious code or breaking behavior without this package being explicitly reviewed, and that risk is somewhat elevated here because the skill handles crypto/payment functionality where dependency trust is especially important.

Content

Scanner excerpt · package.json (reported line 22)May include surrounding context.

json
"author": "Lumen <https://x.com/LumenFTFuture>",
  "license": "MIT",
  "dependencies": {
    "viem": "^2.0.0"
  },
  "repository": {
    "type": "git",

Static analysis

No suspicious patterns detected.