Back to skill

Security audit

x402-CLI

Security checks for vulnerabilities and agentic risk

Overview

The skill is transparent about enabling agents to spend real cryptocurrency, but it gives broad non-confirmed payment and network authority that users should review carefully before installing.

Install only with a fresh, minimally funded wallet and an external approval or policy layer that verifies the exact URL, recipient, network, asset, amount, and maximum spend before every `request pay`. Do not expose personal wallets or broad secrets to this process, avoid arbitrary/internal URLs, be cautious with `--header` and `--data`, and avoid `--save` when request bodies or responses may contain sensitive data.

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

T09 · Insecure Skill Coding Practices

Warning
Location
x402_cli.py:197
Finding
Undefined Runtime Type Annotation Prevents CLI Startup<![CDATA[ ## Vulnerability Details **File Location**: `x402_cli.py:197-204` **Vulnerability Type**: Availability failure caused by an undefined runtime annotation **Risk Level**: Medium ### Vulnerable Code ```python def _x402_request( x402_client: x402ClientSync, url: str, request_header: Optional[Dict[str, Any]] = None, request_type: str = "post", request_data: Optional[Dict[str, Any]] = None, timeout: int = 60, ) -> Tuple[int, Dict[str, Any]]: ``` ### Technical Analysis `x402ClientSync` is neither defined in the module nor imported from a dependency. The file also does not enable postponed evaluation through `from __future__ import annotations`. Consequently, Python evaluates the annotation while defining `_x402_request` and raises `NameError` during module loading. This occurs before `main()` runs and before the CLI can emit its documented structured error response. The issue renders all commands unavailable, including commands that do not perform payments, such as `discover list`, `discover search`, and `request info`. ### Attack Path 1. A user or Agent invokes any command, such as: ```bash python x402_cli.py discover list ``` 2. Python imports and evaluates the module. 3. Execution reaches the `_x402_request` function definition. 4. Python attempts to resolve `x402ClientSync`. 5. Because the name is undefined, module loading terminates with `NameError`. 6. The requested operation never executes, and the documented JSON output contract is bypassed. No attacker-controlled input is required; this is an unconditional availability defect. ### Impact Assessment - Complete denial of service for all advertised CLI functionality. - Discovery, endpoint inspection, and payment operations cannot execute. - Automation expecting one JSON object receives an unstructured interpreter exception instead. - No additional system privilege is obtained by an attacker. - The sensitive network and payment paths are unreachable in the audi ...[truncated 49 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `x402ClientSync` with the imported and correct client type if `x402Client` is intended: ```python def _x402_request( x402_client: x402Client, ... ) -> Tuple[int, Dict[str, Any]]: ``` 2. Alternatively, import the intended synchronous client class explicitly. 3. Consider adding: ```python from __future__ import annotations ``` This prevents immediate annotation evaluation, but it should not substitute for using the correct type. 4. Add startup and smoke tests that import the module and execute every parser path. 5. Add static type checking to CI so undefined annotation names fail before release. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
x402_cli.py:141
Finding
Unrestricted Destination URLs Enable Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `x402_cli.py:141-151` and `x402_cli.py:214-222` **Vulnerability Type**: Unrestricted outbound requests and internal-network access **Risk Level**: High ### Vulnerable Code Plain inspection requests accept the supplied URL without validation: ```python try: match request_type.lower(): case "post": service_response = requests.post(url, headers=header, json=request_data, timeout=timeout) case "get": service_response = requests.get(url, headers=header, params=request_data, timeout=timeout) case _: raise ValueError(f"Unknown request type: {request_type}") ``` Paid requests use the same unrestricted destination: ```python try: match request_type.lower(): case "post": service_response = session.post(url, headers=header, json=request_data, timeout=timeout) case "get": service_response = session.get(url, headers=header, params=request_data, timeout=timeout) case _: raise ValueError(f"Unknown request type: {request_type}") ``` The arbitrary-network capability is explicitly documented in `SKILL.md:212`: ```markdown | Network (arbitrary) | Any URL passed to `request info` / `request pay` | The URL, HTTP method, `--header`, and `--data` you supply are sent as-is to that third-party endpoint — treat it as untrusted. | ``` ### Technical Analysis The CLI performs requests to any caller-selected URL. It does not: - Require HTTPS. - Restrict supported URL schemes explicitly. - Reject loopback, private, link-local, multicast, or reserved IP ranges. - Block cloud instance metadata endpoints. - Restrict destination ports. - Apply a service or hostname allowlist. - Disable or validate redirects. - Re-resolve and validate redirect targets. - Bind a paid destination to a previously inspected public endpoint. Arbitrary access to public x402 services is relevant to the declared functionality. Howev ...[truncated 1896 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `https://` for public x402 endpoints. 2. Parse destinations with a strict URL parser and reject credentials in URLs, malformed hosts, unsupported schemes, and unexpected ports. 3. Resolve the hostname before connecting and reject all loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 ranges. 4. Explicitly block cloud metadata destinations, including link-local metadata addresses. 5. Disable redirects by default. If redirects are necessary, validate every redirect target using the same scheme and address restrictions. 6. Protect against DNS rebinding by validating the address actually used for the connection, not only an earlier DNS result. 7. Support an administrator-configured hostname or service allowlist. 8. Bind `request pay` to a previously inspected URL and verified payment requirements. 9. Avoid automatically forwarding sensitive headers across origins or redirects. 10. Add tests for loopback, IPv6 loopback, private ranges, numeric IP representations, redirects, and DNS rebinding cases. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
x402_cli.py:749
Finding
Payment Spend Limit Accepts Non-Finite, Negative, and Unbounded Values<![CDATA[ ## Vulnerability Details **File Location**: `x402_cli.py:168-180` and `x402_cli.py:749-754` **Vulnerability Type**: Insufficient validation of a financial authorization boundary **Risk Level**: High ### Vulnerable Code The value is multiplied and passed directly to the payment policy: ```python def _init_x402_evm_client(usdc_spend_limit: float) -> x402Client: # Create the eth wallet for the agent to use evm_wallet_secret = os.getenv("CLIENT_EVM_WALLET_SECRET") if evm_wallet_secret is None: raise CliError( "CLIENT_EVM_WALLET_SECRET is not set; set it via a .env file or the shell environment.", ErrorCode.MISSING_ENV_VAR, ) evm_wallet = Account.from_key(evm_wallet_secret) # Init the x402 client x402_client = x402Client().register_policy(max_amount(usdc_spend_limit * 10**USDC_DECIMALS)) ``` Argument parsing imposes no range, finiteness, or precision constraints: ```python pay_parser.add_argument( "--spend-limit", type=float, default=DEFAULT_USDC_SPEND_LIMIT, help="Maximum USDC to authorize for this payment request", ) ``` ### Technical Analysis The financial limit is parsed as a binary floating-point value and is accepted without checking that it is: - Finite. - Greater than zero. - Within a system-level maximum. - Representable precisely in six-decimal USDC units. - Safe for conversion to the amount format expected by `max_amount`. Inputs such as negative values, `nan`, `inf`, or unexpectedly large values can reach the third-party payment-policy implementation. Exact behavior then depends on dependency validation and conversion semantics. Even for ordinary finite values, no administrator-controlled hard ceiling exists. A manipulated Agent can override the documented default of 1 USDC and authorize a much larger amount. The warning is informational and does not require confirmation. ### Attack Path 1. An attacker manipulates the Agent or workflow that constructs t ...[truncated 1339 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse monetary values with `decimal.Decimal`, not `float`. 2. Reject non-finite, zero, and negative values. 3. Enforce no more than six decimal places for USDC. 4. Convert to integer smallest units using explicit, checked rounding. 5. Enforce an administrator-configured hard maximum that command-line input cannot override. 6. Consider separate per-request, per-session, and daily cumulative limits. 7. Require human or policy-engine approval above a conservative threshold. 8. Validate the final integer amount before passing it to `max_amount`. 9. Add tests for `nan`, positive and negative infinity, negative zero, negative values, excessive precision, overflow, and extremely large values. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
x402_cli.py:308
Finding
Sensitive Saved Responses Use Predictable Names and Insecure File Creation<![CDATA[ ## Vulnerability Details **File Location**: `x402_cli.py:308-323` **Vulnerability Type**: Insecure local storage and symbolic-link overwrite risk **Risk Level**: Medium ### Vulnerable Code ```python def _generate_discovery_filename(command_name: str) -> str: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") return f"x402_{command_name}_{timestamp}.json" def _save_discovery_output(output: Any, command_name: str, output_dir: Optional[str] = None) -> str: target_dir = Path(output_dir or ".") target_dir.mkdir(parents=True, exist_ok=True) output_path = target_dir / _generate_discovery_filename(command_name) with output_path.open("w", encoding="utf-8") as file: json.dump(output, file, indent=2) file.write("\n") return str(output_path) ``` Sensitive request and response data is passed into this generic save routine: ```python payload = { "input": {"header": _redact_sensitive_headers(request_header), "data": request_data}, "response": response, } return _save_discovery_output(payload, command_name, output_dir) ``` ### Technical Analysis The filename is predictable to one-second resolution, and the file is opened with ordinary write mode. This behavior: - Follows symbolic links. - Overwrites an existing path. - Does not use exclusive creation. - Does not explicitly set owner-only permissions. - Can collide when multiple operations save within the same second. - Creates parent directories without validating whether they are trusted or shared. Although common authentication header values are redacted, request bodies and service responses are deliberately stored without redaction. Those fields may contain credentials, personal data, internal API results, or paid content. The feature is opt-in and accompanied by a warning, which reduces accidental exposure but does not address unsafe file creation. ### Attack Path 1. An attacker has write access to the selected output directory, such as a shared ...[truncated 1133 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate cryptographically random filenames rather than timestamp-only names. 2. Create files atomically and exclusively using flags equivalent to `O_CREAT | O_EXCL | O_NOFOLLOW`. 3. Set permissions explicitly to `0600`. 4. Reject symbolic links and validate that the resolved output directory is trusted. 5. Refuse shared or world-writable output directories unless the user explicitly overrides the protection. 6. Avoid automatic parent-directory creation for sensitive output, or create directories with mode `0700`. 7. Add optional body and response redaction, not only header redaction. 8. Consider encryption for payment ledgers and sensitive responses. 9. Prevent same-second collisions and test concurrent save operations. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:54
Finding
Wallet Workflow Relies on Unverified Third-Party Packages and Skills<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:54`, `SKILL.md:296-327`, and `requirements.txt:1-42` **Vulnerability Type**: Supply-chain exposure in security-critical wallet tooling **Risk Level**: Medium ### Vulnerable Guidance The installation procedure executes packages from configured Python package indexes without hash verification: ```bash python -m pip install -r requirements.txt ``` The instructions also install separate Skills that are not included in this project: ```bash openclaw skills install @beocca/create-crypto-wallets ``` ```bash openclaw skills install @beocca/keepass-cli ``` Those external tools are then trusted with private wallet material: ```bash PRIVATE_KEY=$(python keepass_cli.py show-entry --title "x402-wallet" --show-secrets | jq -r '.password') # Set and use export CLIENT_EVM_WALLET_SECRET="$PRIVATE_KEY" python x402_cli.py discover list --limit 5 ``` The requirements file pins versions but does not provide package hashes, for example: ```text eth-account==0.13.7 python-dotenv==1.2.2 requests==2.34.2 web3==7.16.0 x402[evm]==2.14.0 ``` ### Technical Analysis Version pinning improves reproducibility but does not verify artifact integrity. Installation still trusts the configured package index, package maintainers, dependency metadata, and all transitive packages. The external `create-crypto-wallets` and `keepass-cli` Skills are not part of the audited artifact and are not pinned in the documented commands to immutable versions or content digests. They are instructed to generate, store, retrieve, and reveal wallet private keys. A compromised package, publisher account, package index, referenced Skill, or transitive dependency could execute code during installation or later runtime. Because the main CLI reads a full private key from the environment, imported payment dependencies execute in a process that has wallet authority. No evidence was found that the currently named packages or referenced Skills are ma ...[truncated 1254 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate a fully resolved lockfile with hashes for every package and platform artifact. 2. Install with hash enforcement, such as: ```bash pip install --require-hashes -r requirements.txt ``` 3. Use a trusted, controlled package index or an internally mirrored repository. 4. Audit security-critical dependencies, especially `x402`, `eth-account`, and wallet-signing components. 5. Pin referenced Skills to immutable versions or content digests. 6. Include and audit wallet-management tooling locally rather than relying on mutable installation identifiers. 7. Verify package signatures or provenance attestations where supported. 8. Run wallet tooling in a constrained environment with minimal file and network access. 9. Avoid exposing plaintext private keys through reusable shell variables and command substitutions. 10. Prefer a dedicated hardware-backed or policy-enforcing signer so third-party Python code never receives the raw private key. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (15)

Credential Access

High
Category
Privilege Escalation
Content
evm_wallet_secret = os.getenv("CLIENT_EVM_WALLET_SECRET")
    if evm_wallet_secret is None:
        raise CliError(
            "CLIENT_EVM_WALLET_SECRET is not set; set it via a .env file or the shell environment.",
            ErrorCode.MISSING_ENV_VAR,
        )
    evm_wallet = Account.from_key(evm_wallet_secret)
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
This CLI enables autonomous spending of real cryptocurrency (USDC on Base mainnet). When you invoke `request pay`, funds are moved irreversibly. This is not a simulation or test environment—transactions are final and cannot be reversed.

## 2. No Confirmation Gate in the CLI

The CLI itself provides no confirmation prompt or approval workflow before sending payment. A warning is printed to stderr before payment, but this does not block execution. If you require human approval, implement that gate in your agent orchestration layer *before* calling the CLI.
Confidence
95% confidence
Finding
The disclosure confirms the CLI can autonomously initiate irreversible cryptocurrency payments without a blocking confirmation or built-in approval control. In an agent context, this is dangerous because instruction misunderstanding, prompt injection, or malicious service selection can directly cause real financial loss with no recovery path.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
## 2. No Confirmation Gate in the CLI

The CLI itself provides no confirmation prompt or approval workflow before sending payment. A warning is printed to stderr before payment, but this does not block execution. If you require human approval, implement that gate in your agent orchestration layer *before* calling the CLI.

## 3. Irreversible Blockchain Transactions
Confidence
97% confidence
Finding
This finding documents that payment execution proceeds even though only a warning is printed to stderr, meaning there is no effective safeguard against autonomous or accidental spending. Because the skill is specifically designed for agent use and handles real funds, the absence of an approval barrier materially increases the likelihood of unauthorized or mistaken transactions.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill declares powerful capabilities in prose, but does not enforce them through a formal permission or allowed-tools scope. That creates a governance gap: hosts or reviewers cannot reliably constrain environment access, network use, and file writes based on machine-readable policy, increasing the chance of over-privileged execution or unsafe composition with other agent tooling.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
|---|---|---|
| `--spend-limit` | `1.0` | Maximum USDC to authorize for the payment request |

**⚠️ This authorizes an on-chain payment the moment it runs — there is no confirmation prompt.** A warning is printed to stderr immediately beforehand as a last reminder, but it does not block execution. Always inspect with `request info` first.

## Output Contract
Confidence
97% confidence
Finding
The skill explicitly enables irreversible on-chain payments without a confirmation gate. In an autonomous agent setting, this is dangerous because prompt injection, malicious discovery results, operator mistakes, or compromised orchestration can directly trigger financial loss with no final approval checkpoint.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
→ confirm scheme/network/amount/payTo match what you expect
        → confirm input/output format via extensions.bazaar in the discovery entry
              ↓
3. request pay <resource_url>            (moves real funds — no confirmation prompt)
        → signs payment with CLIENT_EVM_WALLET_SECRET, sends it, returns the service response
```
Confidence
97% confidence
Finding
This workflow normalizes autonomous payment execution after discovery and inspection, but still leaves the final spend action non-interactive and fully agent-driven. Because discovery results and remote payment terms come from untrusted external services, an agent can be manipulated into sending funds to attacker-controlled endpoints or paying for unintended services.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
## Networks & Security Model

- **Base mainnet only** (`eip155:8453`). Testnets are not supported and will fail — confirm your wallet holds Base-mainnet funds before calling `request pay`.
- **No confirmation gate by design.** This skill is built for autonomous use: an agent can discover, inspect, and pay without stopping for human approval. The risk boundary is the wallet's funding limit, not the CLI. Wrap it in your own orchestration if you need a human-in-the-loop step.

### Capabilities (explicit declaration)
Confidence
98% confidence
Finding
The documented security model deliberately places the risk boundary at wallet balance instead of transaction authorization controls. That is unsafe in many agent deployments because bounded funds limit maximum loss but do not prevent unauthorized or adversarially induced payments, making theft or abuse expected rather than exceptional when the agent is exposed to untrusted inputs.

External Transmission

Medium
Category
Data Exfiltration
Content
Returns:
        The parsed JSON catalog response (dict or list).
    """
    base_url = "https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources"

    params = {"limit": limit, "offset": offset}
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
Returns:
        The parsed JSON catalog response (dict or list).
    """
    base_url = "https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources"

    params = {"limit": limit, "offset": offset}
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
try:
        match request_type.lower():
            case "post":
                service_response = requests.post(url, headers=header, json=request_data, timeout=timeout)
            case "get":
                service_response = requests.get(url, headers=header, params=request_data, timeout=timeout)
            case _:
Confidence
95% confidence
Finding
The CLI sends arbitrary user-supplied headers and request bodies to an arbitrary URL, which can exfiltrate secrets or sensitive data to third parties and can be abused for SSRF-style access to internal services if the agent can reach them. In this skill context, that risk is amplified because the tool is explicitly designed for agent automation and can combine external transmission with immediate paid requests.

External Transmission

Medium
Category
Data Exfiltration
Content
"resources": {
            "items": [
                {
                    "resource": "https://api.example.com/service",
                    "description": "Service description",
                    "accepts": [
                        {
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
"resources": {
            "items": [
                {
                    "resource": "https://api.example.com/service",
                    "description": "Service description",
                    "accepts": [
                        {
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
"resources": {
            "items": [
                {
                    "resource": "https://api.example.com/service",
                    "description": "Service description",
                    "accepts": [
                        {
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
5. Returns the service response
    
    WARNING: THIS SPENDS REAL MONEY ON BASE MAINNET IMMEDIATELY
    No confirmation prompt is shown — the payment is authorized the moment this runs.
    Always call request info first to verify the endpoint.
    
    Example output structure (successful payment + service response):
Confidence
97% confidence
Finding
The tool can autonomously authorize blockchain payments to third-party endpoints without an interactive confirmation step, creating a direct path from agent prompt/input to irreversible real-money spend. In an agent setting this is especially dangerous because untrusted instructions or discovered services could trigger unwanted transactions before human review.

Scope Creep

Low
Category
Excessive Agency
Content
- Overpay for a service
- Interact with defective, unavailable, or malicious endpoints

**Users are solely responsible for implementing appropriate safeguards outside this software**, including but not limited to:
- Wallet-level spend limits and policies
- Service allowlists and address allowlists
- Network restrictions and RPC provider selection
Confidence
70% confidence
Finding
Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Static analysis

No suspicious patterns detected.