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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

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

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

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 155)May include surrounding context.

md
// 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

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

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

md
| 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 |