Back to skill

Security audit

x402 Paywall Kit

Security checks for vulnerabilities and agentic risk

Overview

This skill is purpose-aligned for x402 payments, but it asks for real wallet signing authority and has unresolved safety and package-identity issues users should review before installing.

Install only after confirming the npm packages are published by the expected owner and match this source. Use a dedicated low-balance wallet, start on testnet, require human approval on mainnet, set non-empty domain and recipient allowlists, and avoid relying on the current policy engine until malformed and negative amount handling is fixed.

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

T09 · Insecure Skill Coding Practices

Error
Location
packages/shared/src/policy/index.ts:49
Finding
Payment policy fails open for malformed, non-finite, or negative amounts<![CDATA[ ## Vulnerability Details **File Location**: `packages/shared/src/policy/index.ts:49-82, 172`; supporting conversion in `packages/agent/src/interceptor.ts:48-51, 139-142` **Vulnerability Type**: Improper numeric input validation in a financial authorization boundary **Risk Level**: High ### Vulnerable Code ```typescript // packages/agent/src/interceptor.ts:48-51 function rawToHuman(rawAmount: string, decimals: number): string { const raw = Number(rawAmount); return (raw / 10 ** decimals).toString(); } ``` ```typescript // packages/agent/src/interceptor.ts:139-142 const humanAmount = rawToHuman(matchingReq.amount, decimals); // 5. Check policy const domain = new URL(url).hostname; ``` ```typescript // packages/shared/src/policy/index.ts:49-82 const amountNum = parseFloat(amount); const maxPerRequest = parseFloat(policy.maxPerRequest); const maxDailySpend = parseFloat(policy.maxDailySpend); // 5. Per-request limit if (amountNum > maxPerRequest) { if (policy.requireHumanApproval) { return { decision: "needs-human-approval", reason: `Amount ${amount} exceeds per-request limit of ${policy.maxPerRequest}`, }; } return { decision: "denied", reason: `Amount ${amount} exceeds per-request limit of ${policy.maxPerRequest}`, }; } // 6. Daily spend limit if (currentDailySpend + amountNum > maxDailySpend) { if (policy.requireHumanApproval) { return { decision: "needs-human-approval", reason: `Daily spend would be ${currentDailySpend + amountNum}, exceeding limit of ${policy.maxDailySpend}`, }; } return { decision: "denied", reason: `Daily spend would be ${currentDailySpend + amountNum}, exceeding limit of ${policy.maxDailySpend}`, }; } // 7. All checks passed return { decision: "approved" }; ``` ```typescript // packages/shared/src/policy/index.ts:172 dailySpend += parseFloat(amount); ``` ### Technical Analysis The amount in an x402 payment requirement is controlled by the remote e ...[truncated 1972 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Represent raw token amounts with `bigint` and perform all policy comparisons in smallest token units. - Reject values unless they match a strict unsigned-integer grammar, such as `^[0-9]+$`, before conversion. - Explicitly reject empty, signed, fractional, exponential, non-finite, negative, overflowing, and otherwise malformed amounts. - Validate `maxPerRequest`, `maxDailySpend`, and `tokenDecimals` when configuration is initialized. - Return a denied decision on every parsing or arithmetic failure. - Prevent `recordSpend()` from accepting invalid or negative values. - Add tests covering `NaN`, `Infinity`, `-Infinity`, negative values, exponential notation, overflow, empty strings, trailing characters, and values exceeding safe integer precision. - Consider atomically reserving an approved amount before signing and reconciling that reservation after settlement. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:39
Finding
Installation instructions reference packages that do not match the audited workspace identities<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:39-48`; package identity evidence in `packages/agent/package.json:1-3` and `packages/shared/package.json:1-3` **Vulnerability Type**: Dependency identity mismatch and potential dependency-confusion exposure **Risk Level**: Medium ### Vulnerable Code ```markdown <!-- SKILL.md:39-48 --> npm install @x402-kit/agent @x402-kit/shared ``` ```typescript import { createAgentFetch } from "@x402-kit/agent"; const agentFetch = createAgentFetch({ walletPrivateKey: process.env.X402_WALLET_PRIVATE_KEY as `0x${string}`, ``` ```json // packages/agent/package.json:1-3 { "name": "x402-kit-agent", "version": "0.1.0", ``` ```json // packages/shared/package.json:1-3 { "name": "x402-kit-shared", "version": "0.1.0", ``` The lockfile confirms the same unscoped workspace identities: ```json // package-lock.json:7901-7913 "packages/agent": { "name": "x402-kit-agent", "version": "0.1.0", "license": "MIT", "dependencies": { "@x402/core": "^2.5.0", "@x402/evm": "^2.5.0", "@x402/fetch": "^2.5.0", "viem": "^2.0.0", "x402-kit-shared": "*" } } ``` ### Technical Analysis The Skill instructs users to install and import `@x402-kit/agent` and `@x402-kit/shared`, but the code reviewed in the repository is declared as `x402-kit-agent` and `x402-kit-shared`. Therefore, following the Skill instructions does not necessarily install the implementation that was audited. This is especially sensitive because the installed agent package runs in a process containing `X402_WALLET_PRIVATE_KEY` and receives that secret as a configuration value. If the scoped packages are not controlled by the same trusted publisher, or if their published contents differ from this repository, a substituted package could access the private key and perform arbitrary actions available to the Node.js process. The audit did not establish that the scoped packages are malicious. The confirmed defect is the package-identity ...[truncated 1178 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Make package names identical across package manifests, imports, lockfile workspace entries, README files, and Skill instructions. - If the intended names are scoped, rename the package manifests to `@x402-kit/agent`, `@x402-kit/shared`, and the corresponding Express package before publication. - Publish only through an organization-controlled npm scope with mandatory multifactor authentication and restricted publishing tokens. - Pin instructions to reviewed versions instead of using an unqualified latest release. - Publish provenance attestations and verify that released artifacts are reproducibly generated from tagged source. - Document the expected npm publisher, package integrity, repository URL, and release hash. - Avoid exposing a high-value wallet key to a general application process; use a dedicated low-balance wallet or constrained external signer. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:47
Finding
Recommended configurations grant unattended mainnet payment authority to unrestricted domains<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:47-58, 133-145`; more severe uncapped example in `x402-agent-free/SKILL.md:47-82` **Vulnerability Type**: Excessive financial authority and unsafe default configuration **Risk Level**: Medium ### Vulnerable Code The primary setup uses Base mainnet, disables human approval, and supplies no domain allowlist: ```typescript // SKILL.md:47-58 const agentFetch = createAgentFetch({ walletPrivateKey: process.env.X402_WALLET_PRIVATE_KEY as `0x${string}`, network: "eip155:8453", // Base mainnet policy: { maxPerRequest: "1.00", // Max 1 USDC per request maxDailySpend: "10.00", // Max 10 USDC per day allowedNetworks: ["eip155:8453"], allowedAssets: ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"], // USDC on Base requireHumanApproval: false, }, logFilePath: "./x402-payments.jsonl", }); ``` The basic mainnet example repeats the same unrestricted policy: ```typescript // SKILL.md:133-145 const agentFetch = createAgentFetch({ walletPrivateKey: process.env.X402_WALLET_PRIVATE_KEY as `0x${string}`, network: "eip155:8453", policy: { maxPerRequest: "0.50", maxDailySpend: "5.00", allowedNetworks: ["eip155:8453"], allowedAssets: ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"], requireHumanApproval: false, }, }); const response = await agentFetch("https://api.example.com/premium/weather"); ``` The free edition creates an automatic signer without any local spending or domain policy: ```typescript // x402-agent-free/SKILL.md:47-82 import { createWalletClient, http } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { base } from "viem/chains"; import { wrapFetchWithPayment } from "@x402/fetch"; import { toClientEvmSigner, ExactEvmScheme } from "@x402/evm"; // Load wallet from environment (never hardcode!) const account = privateKeyToAccount(process.env.X402_WALLET_PRIVATE_KEY as `0x${string}`); const walletClient = cre ...[truncated 2594 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Make testnet the default in all introductory examples. - Require a non-empty domain allowlist before enabling automatic mainnet payments. - Add a recipient-address allowlist and verify the payment recipient before signing. - Require explicit human approval on mainnet by default, especially for first-time domains or recipients. - Apply per-request, daily, and lifetime/session limits in every edition, including the free edition. - Use a dedicated low-balance wallet rather than a wallet holding unrelated assets. - Reject cross-origin redirects during paid requests unless the destination is separately allowlisted. - Display or log the exact domain, recipient, amount, asset, network, and authorization expiry before approval. - Consider a signer service that enforces policy independently of the application process. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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
Findings (110)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
This code chunk’s primary purpose is software bundling and packaging, not paywall detection or payment automation. The declared description says the skill should handle x402 402-payment responses and pay via Coinbase/Base/USDC, but the script only builds artifacts and archives files. That is a material description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The code is clearly related to x402 paywall payment, so the general domain matches. However, the declared description presents a general skill that detects and pays x402 paywalls automatically for agent requests, specifically via a Coinbase facilitator on Base with USDC. The actual code chunk is a demo program for one endpoint (/api/joke) on a demo server, with an explicit health check and fixed policy configuration. It uses Base Sepolia testnet (chain 84532) and a locally supplied wallet private key, and the code shown does not demonstrate any Coinbase facilitator integration. These are material differences in primary scope and payment method, so this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is for a consumer/client skill that reacts to 402 Payment Required responses and automatically completes payment on behalf of an agent. The supplied code does the opposite role: it is a server-side demo that hosts a paywalled endpoint and issues 402 responses until payment is made. It also depends on an environment variable for the recipient wallet and logs server-side payment activity. While both relate to the x402 ecosystem, the primary purpose and behavior are materially different.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is about runtime payment-handling behavior for x402-protected endpoints. The supplied code chunk does not implement any of that functionality; it only configures the TypeScript build process using tsup. There is no network handling, no parsing of 402 responses, no crypto payment flow, and no interaction with Coinbase, Base, or USDC. This is a materially different primary purpose, so it is a clear mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
There is a clear mismatch between the declared purpose and the actual code. The description claims operational functionality for detecting and paying x402 crypto paywalls, but the code chunk is merely a test configuration file for Vitest. Its primary purpose is unrelated project/test setup, with no visible logic for HTTP 402 handling, x402 JSON parsing, payment execution, wallet/network interaction, or endpoint access.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description is for a client/agent capability that notices 402 responses and pays x402 paywalls automatically. The supplied code chunk is instead a test suite for Express middleware utilities. It validates route config transformation, middleware construction, logging options, and basic request routing behavior. While x402 concepts, facilitator URLs, and Base network defaults appear, the code shown is about server-side middleware setup/tests, not the advertised automatic payment behavior. Therefore the description does not accurately represent this code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description is for a client-side capability that responds to 402 Payment Required errors by paying x402 paywalls automatically. The actual code chunk is a minimal barrel file for an Express package that exports middleware and re-exports functions from another package. Its primary purpose appears to be exposing server-side Express middleware components, not performing autonomous payment on receipt of paid API responses. While the underlying middleware package may be related to x402, this specific code does not substantiate the claimed behavior, and the server-side Express integration focus materially differs from the declared automatic payment skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The description says the skill is for an agent consuming paid APIs: it should detect 402 Payment Required responses and automatically pay them. The code instead builds Express middleware for a service provider to place x402 paywalls on its own routes. It sets route pricing and recipient addresses, creates an x402 resource server, registers the exact EVM payment scheme, and hooks settlement events for logging. This is materially different in primary purpose and trigger: it is used when building a paid server endpoint, not when an agent hits one. The code also does not specifically implement Coinbase facilitator or USDC-only payment behavior; those aspects are merely configurable or implied by upstream defaults, not enforced here.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is about runtime behavior for detecting and paying x402 crypto paywalls. The actual code chunk does not implement any payment, HTTP response handling, blockchain interaction, or x402 protocol logic. It is solely a build configuration file for tsup, used to compile/package code. That is a materially different primary purpose, so this is a clear mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
There is a clear mismatch between the declared description and the actual code. The description claims functionality for detecting and paying x402 crypto paywalls, but the code only configures a test runner (Vitest) for a Node environment in an Express package. This is not a supporting implementation detail of the claimed payment capability; it is unrelated infrastructure/test configuration and does not implement any of the declared behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This code chunk does not implement or demonstrate automatic x402 paywall detection or payment handling. Instead, it tests a logger that persists payment-related records to JSONL files on the local filesystem and reads them back. While the logged fields reference x402-related concepts like facilitator, network, asset, and payment success, those are only data fields in log entries, not evidence of actual paywall payment functionality. The actual primary purpose of this chunk is payment logging test coverage, which is materially different from the declared skill purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description emphasizes automatic payment of x402 paywalls, including reacting to HTTP 402 responses and executing crypto payments via Coinbase on Base with USDC. The provided code does not implement or invoke that behavior. It is only a unit test suite for policy-related functions (`evaluate` and `createPolicyEngine`) that decide whether a payment would be approved, denied, or require human approval, and that track/persist daily spend. While such policy checks could support a payment skill, this chunk’s actual behavior is materially different from the declared primary purpose and lacks the core payment, paywall detection, and facilitator interaction capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description promises a concrete payment-handling skill for x402 paywalls, including detection of 402 responses and automated payment on a specific network/provider. The supplied code chunk does not show that behavior. It merely exports shared types plus policy and logging modules from a package index. While some exported names mention payment logging, there is no visible implementation of x402 parsing, 402-response handling, Coinbase facilitation, blockchain/network interaction, or USDC payments. Therefore the code chunk's observable purpose is a shared library barrel file, which is materially different from the declared operational payment skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared purpose is an automatic x402 paywall payment skill, but this code chunk does not perform any network, payment, blockchain, or response-handling logic. Its sole behavior is creating directories, appending serialized payment log entries to a local JSONL file, and reading/parsing them back. While logging could be a supporting detail in a broader payment skill, this chunk by itself materially differs from the declared purpose and introduces filesystem access that is not reflected in the description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared purpose is an automatic x402 paywall payment skill, but the supplied code chunk is only a policy engine component. It evaluates whether a payment should be approved based on domain, network, asset, and spending limits, and tracks daily spend with filesystem persistence. There is no logic for handling HTTP responses, detecting 402 status codes, parsing x402 payloads, contacting Coinbase, or submitting payments on Base with USDC. The code is related to the broader x402 payment domain, but its actual function is materially narrower and different from the declared primary purpose, so this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes a runtime payment-processing skill for x402-protected endpoints. The supplied code chunk does not implement any payment, networking, blockchain, or response-handling behavior. Instead, it is solely a build configuration file defining entry points, output formats, declaration generation, and file extensions for packaging a TypeScript project. This is materially unrelated to the declared purpose, so it is a clear mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
There is a clear mismatch between the description and the code. The declared purpose describes a runtime capability for detecting x402 paywalls and automatically making crypto payments. The actual code is a simple Vitest configuration file that sets up a test project named 'shared' with a Node environment. It does not implement paywall detection, payment flows, network interactions, crypto handling, or any related triggers. The primary purpose of the code is test configuration, which is materially different from the declared skill behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description says this skill should automatically pay x402 crypto paywalls encountered by an agent, specifically via Coinbase on Base using USDC after receiving a 402 Payment Required response. The provided code does something materially different: it is a Stripe checkout integration for selling a product called the 'x402 Paywall Kit' on a website. It creates Stripe Checkout sessions, uses card payments, sets success/cancel URLs, and mentions webhook handling to email a download link after purchase. There is no logic for inspecting API responses, parsing x402 JSON, paying remote paywalls, interacting with Coinbase, using crypto, or handling Base/USDC. This is a clear description-behavior mismatch with a different primary purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description says this skill should help an agent consume paid x402 resources by detecting 402 responses and automatically making USDC payments through Coinbase on Base. The provided code does the opposite role: it configures an endpoint to require x402 payment for access to a product download. It contains example Express middleware setup and a static paywall config object with price, recipient, network, description, and MIME type. There is no logic for observing outbound HTTP responses, parsing x402 JSON from a 402 response, initiating payment, using a Coinbase facilitator, or retrying access after payment. This is a materially different primary purpose, so it is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description is for an automated x402 payment skill that reacts to 402 Payment Required responses and executes USDC payments through a Coinbase facilitator. The supplied code does not implement any of that behavior. It merely configures Wagmi for wallet connectivity on Base/Base Sepolia, with injected connectors and transports. While this could be a supporting piece for a payment-enabled app, by itself it neither detects x402 paywalls nor performs payments, parses responses, or integrates with any facilitator. Therefore the actual behavior is materially narrower and different from the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes a runtime payment-handling skill for x402-protected endpoints, including detecting 402 responses and executing crypto payments. The actual code only configures the Vitest test runner, specifying test projects and passWithNoTests behavior. It contains no networking, payment, blockchain, Coinbase, Base, USDC, or x402-related functionality. This is a clear material mismatch in primary purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes an operational payment skill for handling x402 crypto paywalls. The supplied code chunk is merely a test configuration file for Vitest, controlling which integration tests run and their timeout. It has a materially different purpose and does not exhibit any of the declared functional capabilities.

Missing User Warnings

High
Confidence
97% confidence
Finding
The free SKILL.md snippet declares `X402_WALLET_PRIVATE_KEY` as required and describes automatic payment handling, yet it omits a clear warning that the skill can spend real funds using that key. In an agent ecosystem, this omission is dangerous because operators may install the skill as routine infrastructure and unknowingly grant autonomous payment authority to any workflow that encounters compatible 402 responses.

Known Vulnerable Dependency: brace-expansion==5.0.4 — 5 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-45149 (brace-expansion: Large numeric range defeats documented `max` DoS protection) +2 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: axios==1.13.6 — 16 advisory(ies): CVE-2026-44494 (axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `co); CVE-2026-44495 (axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollut); CVE-2025-62718 (Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF) +13 more

High
Category
Supply Chain
Confidence
90% confidence
Finding
axios is a runtime dependency of @coinbase/cdp-sdk, which is directly relevant to this skill's network-facing payment facilitation behavior. If exploitable advisories such as SSRF, proxy bypass, credential leakage, or MITM-related issues apply, they are especially concerning here because the skill automatically handles paid API interactions and may process secrets, payment metadata, and outbound requests to third-party endpoints.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
integration/base-sepolia.test.ts:31