Back to skill

Security audit

Sponge Wallet

Security checks for vulnerabilities and agentic risk

Overview

This wallet skill is purpose-aligned but should be reviewed because it can move funds, auto-pay for services, stores wallet credentials locally, and contains unsafe command and endpoint handling around authentication.

Review before installing. Use only with a trusted Sponge endpoint, avoid setting SPONGE_API_URL unless you fully control it, prefer test keys and small balances, treat ~/.spongewallet/credentials.json as a sensitive secret, and manually confirm every transfer, swap, withdrawal, signed payment, and auto-paid API request.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wallet.mjs:103
Finding

OAuth Device Code Shell Command Injection

Content
View full analysis

Vulnerability Details

File Location: scripts/wallet.mjs, lines 103–119
Vulnerability Type: OS command injection through untrusted OAuth response data
Risk Level: High

Vulnerable Code:

js
// Try to copy code to clipboard
let copied = false;
try {
  const { execSync } = await import("node:child_process");
  const platform = process.platform;
  if (platform === "darwin") {
    execSync(`echo -n ${JSON.stringify(device.userCode)} | pbcopy`);
    copied = true;
  } else if (platform === "linux") {
    execSync(`echo -n ${JSON.stringify(device.userCode)} | xclip -selection clipboard 2>/dev/null || echo -n ${JSON.stringify(device.userCode)} | xsel --clipboard 2>/dev/null`);
    copied = true;
  } else if (platform === "win32") {
    execSync(`echo|set /p=${JSON.stringify(device.userCode)} | clip`);
    copied = true;
  }
} catch {
  // Clipboard not available
}

Technical Analysis

device.userCode originates from the JSON response returned by the configured OAuth authorization endpoint. The value is interpolated directly into a command passed to execSync(), which invokes a system shell.

JSON.stringify() serializes JavaScript data but does not provide shell escaping. In particular, command substitutions such as $(command) remain active inside double-quoted strings on Unix-like systems. Shell metacharacter behavior on Windows likewise differs from JSON escaping and cannot be safely neutralized this way.

Consequently, a malicious or compromised OAuth server can return a crafted device code that causes arbitrary local commands to execute when the Skill attempts to copy the code to the clipboard.

Attack Path

  1. An attacker compromises the OAuth service or causes the process to use an attacker-controlled API endpoint through SPONGE_API_URL.
  2. The user or Agent runs node scripts/wallet.mjs login.
  3. The malicious endpoint returns a crafted userCode, such as ...[truncated 778 chars]
Remediation
View remediation

Remediation Suggestions

  • Eliminate shell interpretation. Use spawnSync() or execFileSync() with shell: false and send the device code through the child process's standard input.
  • Prefer a maintained clipboard library that accepts a string directly without constructing shell commands.
  • Validate device.userCode against the OAuth provider's documented format, including a strict character allowlist and maximum length.
  • Treat validation as defense in depth rather than a substitute for removing shell evaluation.
  • Keep clipboard copying optional and report failure without falling back to dynamically constructed shell commands.

Example hardening approach:

js
import { spawnSync } from "node:child_process";

const code = String(device.userCode);
if (!/^[A-Z0-9-]{4,32}$/.test(code)) {
  throw new Error("OAuth server returned an invalid device code.");
}

if (process.platform === "darwin") {
  spawnSync("pbcopy", [], {
    input: code,
    encoding: "utf8",
    shell: false,
    stdio: ["pipe", "ignore", "ignore"],
  });
}

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wallet.mjs:36
Finding

Stored API Key Can Be Redirected to an Arbitrary Network Endpoint

Content
View full analysis

Vulnerability Details

File Location: scripts/wallet.mjs, lines 36–38 and 230–245; the same destination is also used with credentials at lines 276–289
Vulnerability Type: Bearer-token disclosure through an unrestricted endpoint override
Risk Level: High

Vulnerable Code:

js
const DEFAULT_API_URL = "https://api.wallet.paysponge.com";
const API_URL = process.env.SPONGE_API_URL || DEFAULT_API_URL;
const MCP_ENDPOINT = `${API_URL}/mcp`;
js
async function jsonrpc(apiKey, method, params, sessionId) {
  const headers = {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  };
  if (sessionId) {
    headers["Mcp-Session-Id"] = sessionId;
  }

  const res = await fetch(MCP_ENDPOINT, {
    method: "POST",
    headers,
    body: JSON.stringify({
      jsonrpc: "2.0",
      method,
      params,
      id: crypto.randomUUID(),
    }),
  });
js
await fetch(MCP_ENDPOINT, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
    "Mcp-Session-Id": sessionId,
  },
  body: JSON.stringify({
    jsonrpc: "2.0",
    method: "notifications/initialized",
  }),
});

Technical Analysis

SPONGE_API_URL controls the destination used for OAuth and MCP requests without validating the URL scheme or hostname. Authenticated MCP requests attach the API key as a bearer token.

The API key can be loaded from ~/.spongewallet/credentials.json, so setting one environment variable is sufficient to redirect an existing stored credential to another origin. The implementation does not require HTTPS, compare the destination with an allowlist, separate custom-server credentials from production credentials, or request confirmation before transmitting the key.

Althoug ...[truncated 1685 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove SPONGE_API_URL from production builds unless custom endpoints are an explicit requirement.
  • If endpoint customization is required, parse the value with new URL() and require HTTPS.
  • Enforce an explicit hostname allowlist for any request carrying a production credential.
  • Bind stored credentials to the origin that issued them. Refuse to send a credential when the active endpoint does not exactly match the stored origin.
  • Store custom-development endpoint credentials separately from production credentials.
  • Require explicit, visible user confirmation before authenticating to or sending a credential to a non-default endpoint.
  • Reject URLs containing embedded credentials, unexpected ports, non-empty fragments, or schemes other than HTTPS.
  • Consider certificate pinning or equivalent endpoint-authentication controls where the deployment model supports them.
  • Rotate any API key suspected of having been used while an untrusted SPONGE_API_URL was configured.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/wallet.mjs:201
Finding

Authentication Status Command Exposes an Excessive API-Key Prefix

Content
View full analysis

Vulnerability Details

File Location: scripts/wallet.mjs, lines 201–216
Vulnerability Type: Partial secret disclosure through terminal and Agent output
Risk Level: Low

Vulnerable Code:

js
function whoami() {
  const envKey = process.env.SPONGE_API_KEY;
  const creds = loadCredentials();

  if (envKey) {
    const prefix = envKey.substring(0, 20);
    const isTest = envKey.startsWith("sponge_test_");
    console.log(`Authenticated via SPONGE_API_KEY environment variable`);
    console.log(`  Key: ${prefix}...`);
    console.log(`  Mode: ${isTest ? "testnet" : "mainnet"}`);
  } else if (creds) {
    const prefix = creds.apiKey.substring(0, 20);
    const isTest = creds.apiKey.startsWith("sponge_test_");
    console.log(`Authenticated via stored credentials`);
    console.log(`  Key: ${prefix}...`);
    console.log(`  Mode: ${isTest ? "testnet" : "mainnet"}`);
    console.log(`  File: ${CREDENTIALS_FILE}`);

Technical Analysis

The whoami command prints the first 20 characters of the active API key. This is substantially more secret material than is necessary to identify the authentication mode or distinguish credentials.

Terminal output may be retained in Agent transcripts, CI logs, support bundles, shell recordings, or shared screenshots. Prefix disclosure may also reveal key type and other structured information. While a partial prefix is not ordinarily sufficient for direct authentication, unnecessary disclosure reduces the remaining secret space and increases exposure when combined with another information leak.

Attack Path

  1. The user or Agent invokes node scripts/wallet.mjs whoami.
  2. The command reads the API key from the environment or credentials file.
  3. The first 20 characters are printed to standard output.
  4. The output is recorded in a transcript, log, screenshot, or other accessible artifact.
  5. An unauthorized observer obtains the partial secre ...[truncated 453 chars]
Remediation
View remediation

Remediation Suggestions

  • Do not print any API-key prefix by default.
  • Report only the authentication source and mode, such as “stored credentials” and “testnet.”
  • If credential differentiation is operationally necessary, display no more than the final four characters rather than the first 20.
  • Ensure diagnostic and error paths never include complete bearer tokens.
  • Add automated tests that verify authentication commands redact credentials from standard output and standard error.
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (20)

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The README explicitly identifies a local file containing saved OAuth credentials, which is a credential-access concern because it normalizes storing reusable secrets on disk in a discoverable location. Given this skill can manage wallets, transfers, swaps, and paid services, compromise of that file could lead to account takeover, unauthorized spending, and abuse of linked financial capabilities.

Content

Scanner excerpt · README.md (reported line 21)May include surrounding context.

node ~/.claude/skills/sponge-wallet/scripts/wallet.mjs login

text

This opens a browser for OAuth authentication. Credentials are saved to `~/.spongewallet/credentials.json`.

## What it can do

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill documents token transfers and swaps but does not prominently warn that blockchain actions are irreversible, may incur fees, and can result in permanent asset loss if addresses or parameters are wrong. In the context of a wallet-management skill, omission of that warning materially increases user safety risk because the skill directly supports moving and swapping funds.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The skill explicitly documents storing authentication credentials in ~/.spongewallet/credentials.json, which indicates local credential access and persistence for a wallet-related service. In a crypto-payment context, compromise of those credentials could enable unauthorized API usage, balance access, payment creation, or fund movement depending on backend privileges.

Content

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

text
Use the `device_code`, `interval`, and `expires_in` values from Phase 1 output.

Credentials are saved to `~/.spongewallet/credentials.json` automatically.

Credential resolution order:
1. `SPONGE_API_KEY` environment variable (if set, skips stored credentials)

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

The documented credential resolution order includes both SPONGE_API_KEY from the environment and a stored credentials file, showing the skill can consume sensitive secrets from multiple sources. In an agent setting, broad secret access combined with financial operations increases the blast radius of any misuse or accidental disclosure.

Content

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

md
Credential resolution order:
1. `SPONGE_API_KEY` environment variable (if set, skips stored credentials)
2. `~/.spongewallet/credentials.json` (saved by login)

Other auth commands:
- `node wallet.mjs whoami` — show current auth status

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · scripts/wallet.mjs (reported line 21)May include surrounding context.

js
*
 * Credential resolution order:
 *   1. SPONGE_API_KEY environment variable
 *   2. ~/.spongewallet/credentials.json (saved by `login` command)
 *
 * Environment:
 *   SPONGE_API_KEY  - Optional. Overrides stored credentials.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · scripts/wallet.mjs (reported line 41)May include surrounding context.

js
*
 * Credential resolution order:
 *   1. SPONGE_API_KEY environment variable
 *   2. ~/.spongewallet/credentials.json (saved by `login` command)
 *
 * Environment:
 *   SPONGE_API_KEY  - Optional. Overrides stored credentials.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The README promotes wallet transfers, token swaps, and x402-paid API calls without warning that these actions can spend funds, may be irreversible, and may trigger real onchain transactions or micropayments. In the context of a crypto wallet skill, missing safety disclosures increases the chance of accidental loss because users may treat commands as low-risk assistant actions rather than financial operations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README instructs users to store OAuth credentials in a fixed plaintext path under the home directory without any warning about filesystem permissions, local compromise, backups, or secret handling. In a wallet/payment skill, exposed credentials could enable unauthorized access to wallet operations or paid API spending, so the omission is security-relevant even though the README itself does not exfiltrate data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The reference documents a fund-transfer tool but does not explicitly warn that blockchain transfers are irreversible and move real assets. In a wallet skill, omission of this warning increases the chance that an agent or user will invoke a transfer without appreciating the consequence, especially since examples are provided in a ready-to-run form.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The Solana transfer documentation similarly omits an explicit warning that sending SOL or USDC causes irreversible on-chain fund movement. Because this skill is specifically designed to manage wallets and execute payments, missing warnings materially raises misuse risk rather than being a purely cosmetic documentation issue.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The swap tool performs market actions that can permanently convert assets and expose the wallet to slippage, price impact, and routing risk, yet the documentation does not present these as dangerous operations. In a trading-enabled wallet skill, that omission can lead to unintended conversions or poor execution under volatile conditions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The withdrawal tool moves funds out of the agent wallet without allowlist approval, but the reference lacks an explicit warning that this action transfers assets away from the agent balance. The absence of caution is more dangerous here because the tool bypasses one of the guardrails mentioned for ordinary transfers.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The sponge interface defaults to auto_pay=true and can spend wallet funds on external API requests, yet the documentation does not prominently warn that ordinary-looking actions like search, crawl, parse, image, or LLM calls can trigger automatic micropayments. This is particularly risky because the spend is implicit and spread across many tasks, making accidental or repeated charges more likely.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The low-level payment creation tool produces signed payment payloads for external resources, which is a sensitive authorization action, but the docs only lightly note it is low-level and do not clearly warn that it can authorize spending. In a wallet/payment skill, under-warning around signing and payment authorization is security-relevant because these primitives are easy to misuse or integrate unsafely.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill invokes local Node scripts that can use environment variables and make networked requests, but the manifest does not declare any tool scope or allowed-tools restrictions. That weakens sandboxing and user visibility, increasing the chance the skill is invoked with broader capabilities than necessary, especially given its ability to move funds and call paid external services.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The description says to use the skill for broad categories like wallet balances, token transfers, swaps, blockchain payments, and paid API services, which can cause over-invocation in ambiguous conversations. In a financial skill, accidental invocation is more dangerous because it may lead the agent into auth flows, external API calls, or transaction preparation without sufficiently explicit user intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The authentication section states that credentials are stored at ~/.spongewallet/credentials.json but does not warn that this file contains sensitive authentication material or provide guidance on local secret protection. This is risky because local credential storage can expose wallet or API access if the workstation, home directory, backups, or logs are accessible to other users or processes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The paid API examples send search queries, URLs, documents, and prospecting inputs to external services without disclosing the privacy implications. Because these tasks may transmit proprietary documents, internal URLs, or sensitive research queries off-system, the omission can lead users to expose confidential data unintentionally.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest describes wallet management, token transfers, swaps, balance checks, and paid API access via micropayments. In contrast, the login helper imports child_process and executes OS-specific clipboard commands, which is a local system interaction capability not justified by the stated purpose of a wallet/payment skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The code deletes the stored credentials file immediately when logout is invoked via fs.unlinkSync, with no confirmation prompt or pre-action warning in the function itself. Although the top-level usage comment says logout removes stored credentials, the destructive file operation lacks an in-flow disclosure before execution.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access, suspicious.potential_exfiltration

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/wallet.mjs:110

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/wallet.mjs:37

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
scripts/wallet.mjs:50