Back to skill

Security audit

x402-api-integration

Security checks for vulnerabilities and agentic risk

Overview

This skill is coherent but needs Review because its default payment-gated API example can be deployed publicly while accepting forged payments.

Review before installing or using. Do not deploy the sample as a paid public API until payment verification is implemented fail-closed, every route awaits verification before work runs, facilitator failures reject access, and public exposure includes normal production controls such as authentication where appropriate, rate limits, input limits, logging, and monitoring.

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

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:65
Finding
Payment Verification Bypass Accepts Forged Payment Headers## Vulnerability Details **File Location**: `SKILL.md`, lines 65–76 **Vulnerability Type**: Payment authorization bypass **Risk Level**: High ### Vulnerable Code ```javascript function checkPayment(req, res, path, method, price, description) { const paymentHeader = getPaymentHeader(req); if (!paymentHeader) { const reqs = buildPaymentRequirements(path, method, price); sendJSON(res, 402, { error: 'Payment required', paymentRequired: reqs }, { 'X-PAYMENT-REQUIRED': encodePaymentHeader(reqs) }); return null; } const payload = decodePaymentHeader(paymentHeader); if (!payload) { sendJSON(res, 400, { error: 'Invalid payment header' }); return null; } // Demo mode: accept any valid-looking payment // TODO: Enable facilitator verification in production return true; } ``` ### Technical Analysis The payment gate only Base64-decodes and JSON-parses the attacker-controlled `payment-signature` or `x-payment` header. Successful parsing is treated as proof of payment. The implementation does not verify: - A cryptographic payment signature - Payment settlement or transaction status - The payer or intended recipient - The required amount and asset - The configured blockchain network - The requested resource - Payment expiration - Nonce uniqueness or replay status Consequently, any syntactically valid JSON value that parses to a truthy value can satisfy the payment check. For example, Base64-encoded `{}` is accepted even though it contains no payment evidence. ### Attack Path 1. An attacker identifies the protected `POST /api/your-service` endpoint. 2. The attacker creates an arbitrary JSON object, such as `{}`. 3. The attacker Base64-encodes it as `e30=`. 4. The attacker sends `payment-signature: e30=` or `x-payment: e30=` with the request. 5. `decodePaymentHeader()` parses the forged payload successfully. 6. `checkPayment()` returns `true` without cryptographic or settlement verification. 7. The protected service executes without ...[truncated 646 chars]
Remediation
## Remediation Suggestions - Remove the permissive demo behavior from any publicly exposed deployment. - Fail closed unless the payment has been cryptographically verified and confirmed as settled. - Verify the signature, payer, `payTo` recipient, exact amount, asset, network, resource, expiration, and protocol version. - Add nonce or transaction-identifier tracking to prevent payment replay. - Build the expected payment requirements server-side; never trust requirements supplied by the client. - Treat facilitator timeouts, malformed responses, non-success HTTP statuses, and parsing errors as failed authorization. - Keep demonstration mode explicitly disabled by default and bind demonstration servers to localhost. - Add automated tests confirming rejection of missing, malformed, forged, underpaid, wrong-recipient, wrong-network, expired, and replayed payments.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:153
Finding
Production Verification Example Can Fail Open or Break Payment Processing## Vulnerability Details **File Location**: `SKILL.md`, lines 153–166 **Vulnerability Type**: Incorrect asynchronous authorization integration and runtime incompatibility **Risk Level**: Medium ### Vulnerable Code ```javascript // In checkPayment(), replace demo mode with: async function verifyPayment(payload, requirements) { const body = JSON.stringify({ paymentPayload: payload, paymentRequirements: requirements }); const res = await fetch('https://x402.org/facilitator/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body }); const result = await res.json(); return result.valid === true; } // Then in checkPayment: const valid = await verifyPayment(payload, requirements); if (!valid) { sendJSON(res, 402, { error: 'Payment verification failed' }); return null; } ``` The surrounding payment function and route are documented as: ```javascript function checkPayment(req, res, path, method, price, description) { ``` ```javascript const payment = checkPayment(req, res, '/api/your-service', 'POST', '0.001', 'Your service — $0.001/call'); if (!payment) return; ``` ### Technical Analysis The production replacement introduces `await` into the originally synchronous `checkPayment()` function. Used literally, this produces a syntax error because `await` is not valid in that non-`async` function. If an operator only changes `checkPayment()` to `async` but leaves the documented route unchanged, the call returns a Promise. A Promise is truthy, so `if (!payment) return` does not wait for or enforce the verification result. The protected service can execute while verification is pending, including when verification later returns `false` or rejects. The Skill also claims compatibility with Node.js 16 and zero dependencies. The example relies on global `fetch`, which is not generally available in the stated runtime. This can cause verification to throw at runtime. No error handling or explicit fail-closed behavior is shown ...[truncated 1290 chars]
Remediation
## Remediation Suggestions - Declare `checkPayment()` as `async` and await it at every call site: ```javascript const payment = await checkPayment( req, res, '/api/your-service', 'POST', '0.001', 'Your service — $0.001/call' ); if (!payment) return; ``` - Ensure the containing route remains asynchronous. - For Node.js 16 compatibility, use the built-in `https` module or explicitly change the minimum supported runtime to a Node.js release with stable global `fetch`. - Wrap facilitator communication and response parsing in `try/catch`. - Reject authorization on timeouts, DNS or TLS errors, non-2xx responses, malformed JSON, and any response other than an explicit `valid === true`. - Define the `requirements` object inside `checkPayment()` before verification and derive it entirely from trusted server configuration. - Apply request timeouts and response-size limits to facilitator calls. - Prevent service execution until verification completes successfully. - Add integration tests that confirm rejected and delayed facilitator responses cannot reach the protected operation.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Intent-Code Divergence

Medium
Confidence
99% confidence
Finding
The sample server markets payment-gated API access but the actual checkPayment implementation accepts any decodable payment header and returns success without cryptographic or facilitator-side verification. In practice this lets any client bypass payment entirely, defeating the security and business control the skill claims to enforce and potentially enabling unlimited unauthorized use of any attached service.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The deployment steps instruct users to expose the local API directly to the public via Cloudflare Tunnel without pairing that exposure with authentication, hardening guidance, logging, input validation, or warnings about sensitive data handling. In this skill's context, that can quickly turn a toy example into an Internet-reachable endpoint that is easy to abuse, especially because the sample payment gate is not actually enforced.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
# 1. Save as server.js
# 2. Run
nohup node server.js > server.log 2>&1 &

# 3. Expose via Cloudflare Tunnel (free)
cloudflared tunnel --url http://localhost:3000
Confidence
65% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

External Transmission

Medium
Category
Data Exfiltration
Content
// In checkPayment(), replace demo mode with:
async function verifyPayment(payload, requirements) {
  const body = JSON.stringify({ paymentPayload: payload, paymentRequirements: requirements });
  const res = await fetch('https://x402.org/facilitator/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body
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
| Platform | Cost | Best For |
|----------|------|----------|
| Cloudflare Tunnel | Free | Dev, testing, low traffic |
| Railway | $5/mo | Production, auto-deploy |
| Fly.io | ~$5/mo | Global, containers |
| VPS + Nginx | $5-10/mo | Full control |
Confidence
85% 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.

Static analysis

No suspicious patterns detected.