Back to skill

Security audit

Agent Wallet (Lobster)

Security checks for vulnerabilities and agentic risk

Overview

This is a real wallet/payment skill, but its payment signing path does not sufficiently constrain or disclose the exact transaction terms before signing.

Review this before installing or using it with real funds. Use a dedicated low-balance wallet, prefer testnet first, avoid arbitrary payment URLs, and do not rely on --confirm as proof that the amount, token contract, recipient, and expiration were safely reviewed. The payment code should add strict network/USDC contract checks, local spend limits, recipient validation, exact amount parsing, and informed confirmation of the resolved payment terms before signing.

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

T09 · Insecure Skill Coding Practices

Error
Location
lib/x402-client.mjs:75
Finding

Untrusted x402 Server Controls Signed Payment Authorization Terms

Content
View full analysis
= 2 || String(maxAmount).length > 6) { value = BigInt(maxAmount); } else { value = BigInt(maxAmount) * BigInt(1e6); } const authorization = { from: account.address, to: requirement.payTo || requirement.payToAddress, value, validAfter, validBefore, nonce, }; const domain = { name: requirement.extra?.name || 'USD Coin', version: requirement.extra?.version || '2', chainId, verifyingContract: requirement.asset || requirement.usdcAddress, }; const types = { TransferWithAuthorization: [ ...[truncated 3225 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
lib/crypto.mjs:51
Finding

Ambiguous Amount Parsing Can Produce Excessive Payment Authorizations

Content
View full analysis
= 2 || String(maxAmount).length > 6) { value = BigInt(maxAmount); } else { value = BigInt(maxAmount) * BigInt(1e6); } ``` The duplicate implementation in `x402-client.mjs:74-84` contains the same behavior: ```js // Parse amount const maxAmount = requirement.maxAmountRequired; let value; if (typeof maxAmount === 'string' && maxAmount.includes('.')) { value = BigInt(Math.floor(parseFloat(maxAmount) * 1e6)); } else if (x402Version >= 2 || String(maxAmount).length > 6) { value = BigInt(maxAmount); } else { value = BigInt(maxAmount) * BigInt(1e6); } ``` ### Technical Analysis The code infers whether a payment amount is denominated in whole USDC or base units using protocol version, the presence of a decimal point, and string length. Monetary units should not be inferred from the number of digits. For version 1 inputs with six or fewer digits and no decimal point, the implementation multiplies the supplied value by `1,000,000`. If such an input was already expressed in base units, the resulting authorization is inflated by a factor of one million. Decimal inputs are converted using `parseFloat`, which uses IEEE-754 floating-point arithmetic. This can introduce rounding and precision inconsistencies before conversion to `BigInt`. The parser also lacks strict canonical-format validation and a local maximum amount. ### Attack Path 1. A malicious or incompatible x402 server returns an amount whose unit representation is ambigu ...[truncated 996 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
x402-client.mjs:141
Finding

Alternate x402 Client Signs Payments Without Its Claimed Confirmation Gate

Content
View full analysis
r.scheme === 'exact' && SUPPORTED_NETWORKS[r.network] ); if (!requirement) { throw new Error('No supported x402 payment options found for this endpoint'); } // Create and sign payment const payment = await createPaymentSignature(account, requirement, requirements.x402Version); // Retry with payment header const paidResponse = await fetch(url, { ...options, headers: { ...options.headers, 'X-Payment': encodePaymentHeader(payment), }, }); return paidResponse; } ``` ### Technical Analysis The function documentation states that an explicit confirmation flag is required, but `x402Fetch` never checks `options.confirm` or any equivalent authorization signal. Once the initial request receives an HTTP 402 response containing a supported network and `exact` payment scheme, the function signs automatically. This module is separate from `lib/x402-client.mjs`, which does check `options.confirm`. The duplicate implementations therefore expose inconsistent security behavior. An integrator importing the root-level module may reasonably rely on its documented guarantee and unintentionally enable automatic signing. Although this implement ...[truncated 1132 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
package-lock.json:17
Finding

Cryptographic Dependencies Are Resolved Through a Non-Default Registry Mirror

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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 (8)

Known Vulnerable Dependency: ws==8.20.1 — 1 advisory(ies): CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
93% confidence
Finding

The lockfile pins ws to version 8.20.1, and the provided advisory indicates this version is affected by a memory-exhaustion denial-of-service issue triggered by tiny fragmented frames/data chunks. Even though this is a transitive dependency pulled in via viem, a lockfile-pinned vulnerable package is still a real supply-chain exposure if the application uses WebSocket functionality or processes untrusted network traffic.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · wallet.mjs (reported line 51)May include surrounding context.

js
if (!pk) {
    throw new Error(
      'x402 payments require WALLET_PRIVATE_KEY.\n' +
      'Set it in .env or as an environment variable.'
    );
  }

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The module header says 'Phase 1: Read-only balance queries (safe, no signing)' and frames payment behavior as a later phase. However, getWallet already supports a private-key-based 'full mode' by reading WALLET_PRIVATE_KEY and returning it in the wallet object, which contradicts the documented phase boundary and safety framing.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The function accepts an x402Version parameter and uses it when interpreting the amount, but the returned object always hard-codes x402Version: 1. This can create a version/signature semantic mismatch where downstream consumers validate or process the payload under the wrong protocol version, potentially causing incorrect payment handling, rejected transactions, or signing data under assumptions different from what verifiers enforce.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The implementation contradicts its own safety contract: despite the docstring stating payment handling requires explicit confirmation, x402Fetch automatically signs a payment authorization and retries the request on any 402 response. This lets any contacted endpoint trigger a wallet signature for a payment payload without an execution-time consent gate, which is especially dangerous because the signed authorization can authorize a USDC transfer if later submitted on-chain.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

On a 402 response, the code automatically replays the original request with an added X-Payment header, preserving the original options and body. This can resend sensitive request data or repeat non-idempotent actions to the same endpoint without an explicit warning, compounding privacy and transaction risks when interacting with untrusted or malicious servers.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The JSDoc for payX402 declares @returns {Promise<Response>}, which implies callers receive a Response object directly. In reality, both return paths yield an object containing paid, status, and response, so the documentation contradicts the actual interface and intent of the function.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
92% confidence
Finding

The dependency uses a caret range (^2.50.4), which permits automatic installation of newer minor and patch releases. In a wallet/payment-related skill, this increases supply-chain risk because an unexpected upstream change or compromised release of a blockchain library could alter signing, transaction handling, or payment behavior without explicit review.

Content

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

json
],
  "license": "MIT-0",
  "dependencies": {
    "viem": "^2.50.4"
  }
}