Back to skill

Security audit

wallet balance

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its wallet-balance purpose, but it stores wallet addresses in a shared unauthenticated service that could expose or alter saved addresses if the gateway is reachable.

Review before installing, especially if the gateway could be exposed beyond your machine. Only save wallet addresses you are comfortable storing in a shared local file, keep the service bound to localhost or behind authentication, and expect queried addresses to be sent to Antalpha MCP and public blockchain/price providers.

Vulnerability Patterns
  • 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
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:23
Finding

Forced Branded Output Injection Through Skill Instructions

Content
View full analysis
Remediation
View remediation

T02 · Agent Memory Poisoning

Error
Location
server.js:996
Finding

Unauthenticated Disclosure and Modification of Persistent Wallet Memory

Content
View full analysis
{ const requestId = makeRequestId(); try { const addresses = await readMemoryAddresses(); return res.json(okBody({ addresses, count: addresses.length }, requestId)); } catch { return res.status(500).json(errorBody("INTERNAL_ERROR", "Failed to read memory list.", requestId)); } }); app.post("/agent-skills/v1/memory", async (req, res) => { const requestId = makeRequestId(); try { const body = req.body && typeof req.body === "object" ? req.body : {}; let list = await readMemoryAddresses(); if (body.remove != null) { const rm = String(body.remove).trim(); const normRm = await normalizeInput(rm).catch(() => null); const key = normRm ? normRm.normalizedAddress : rm; list = list.filter((a) => a.toLowerCase() !== key.toLowerCase()); await writeMemoryAddresses(list); return res.json(okBody({ addresses: list, count: list.length, action: "remove" }, requestId)); } const rawAdds = body.add != null ? body.add : body.address != null ? body.address : null; if (rawAdds == null) { return res.status(400).json(errorBody("INVALID_BODY", "Provide add or remove in JSON body.", requestId)); } const toAdd = Array.isArray(rawAdds) ? rawAdds : [rawAdds]; for (const item of toAdd) { const n = await normalizeInput(String(item || "").trim()); if (!list.some((a) => a.toLowerCase() === n.normalizedAddress.toLowerCase())) { list.push(n.normalizedAddress); } } await writeMemoryAddresses(list); return res.json(okBody({ addresses: list, count: list.length, action: "add" }, requestId)); } catch (err) { const code = err?.message || "INTERNAL_ERROR" ...[truncated 3521 chars]
Remediation
View remediation
{ console.log(`[server] listening on ${HOST}:${PORT}`); }); ``` 2. Require authentication on all memory read and write endpoints. 3. Enforce authorization so callers can access only their own stored addresses. 4. Partition stored addresses by authenticated user, tenant, or Agent session instead of using one global file. 5. Avoid returning full wallet addresses from the list endpoint unless required. Return redacted addresses or identifiers where possible. 6. Require explicit proof of ownership or a trusted confirmation flow before modifying persistent memory where the deployment's threat model requires it. 7. Apply rate limits to both `GET` and `POST` memory routes. 8. Add cross-site request protections if browser-based callers can access the service. 9. Configure restrictive filesystem permissions for the memory file and its parent directory. 10. Document that reverse proxies and container deployments must not expose the service publicly without authentication. 11. Add audit logging for memory additions and removals without logging unrelated secrets. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
server.js:307
Finding

Rate Limiting Can Be Bypassed Through Spoofed Proxy Headers or Redis Failure

Content
View full analysis
10) { const err = new Error("RATE_LIMIT_IP"); err.httpStatus = 429; throw err; } if (addrCount > 5) { const err = new Error("RATE_LIMIT_ADDRESS"); err.httpStatus = 429; throw err; } } ``` ```js const ip = req.ip || req.headers["x-forwarded-for"] || "unknown"; ``` ```js try { await Promise.race([ redis.connect(), new Promise((_, reject) => setTimeout(() => reject(new Error("REDIS_CONNECT_TIMEOUT")), 1500)), ]); } catch (err) { console.error("[bootstrap] redis connect failed:", err.message); } ``` ### Technical Analysis Setting Express `trust proxy` to `true` trusts forwarding information supplied through the complete proxy chain. If the service is directly reachable or the actual reverse proxy does not sanitize forwarding headers, a client can supply changing `X-Forwarded-For` values and influence `req.ip`. The rate limiter also immediately returns when Redis is unavailable: ```js if (!redisReady) return; ``` This is a fail-open design. Redis connection failure is explicitly tolerated during startup, after which the server still accepts requests with no effective rate limiting ...[truncated 2037 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (25)

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 · README.md (reported line 37)May include surrounding context.

Quick Start

bash
cp .env.example .env
npm install
npm start

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 · README.md (reported line 117)May include surrounding context.

Quick Start

bash
cp .env.example .env
npm install
npm start

Known Vulnerable Dependency: ws==8.17.1 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
80% confidence
Finding

ws is a real-time networking library handling untrusted remote input, and the cited issues include memory disclosure and memory exhaustion. Even if WebSocket support is only transitively present through ethers, shipping a vulnerable version increases risk if any code path enables WebSocket RPC/providers, and in a wallet service this can expose sensitive process memory or cause service instability.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The README states that wallet queries use external MCP and public providers but does not warn users that submitted wallet addresses will be transmitted to third-party services. Wallet addresses are sensitive financial identifiers, and sending them to external services without an explicit privacy warning can expose user activity and balance lookups beyond the local environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README advertises address memory but does not clearly warn that wallet addresses are stored persistently and can later be queried automatically. For a wallet-related skill, persisted address history is sensitive metadata that can reveal financial relationships or holdings, so omission of a retention/privacy notice increases the risk of users unknowingly exposing their data.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The example phrases include very broad triggers such as "What's in my wallet?" and "查我的余额", which can plausibly appear in normal conversation and may cause the skill to activate unexpectedly. In this skill, unexpected activation is more sensitive because it can query remembered wallet addresses and send them to external providers, creating privacy and data-minimization risks.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill performs network operations and may access environment-backed runtime capabilities, but it declares no explicit tool scope or permissions boundary. That weakens reviewability and policy enforcement, making it easier for the skill to gain or retain broader execution capabilities than users or operators expect.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The remembered-address trigger is broad enough that ordinary phrases like 'check my balance' can activate queries against previously stored wallet addresses without fresh confirmation. This can cause unintended use of persisted sensitive financial identifiers and may surprise users who did not realize memory would be consulted.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
91% confidence
Finding

The fallback behavior sends user-supplied wallet addresses to a public remote endpoint outside the local gateway path. This broadens data exposure to a third party and may leak sensitive financial interest or address-linkage metadata, especially because users may expect balance checks to remain within company-controlled infrastructure.

Content

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

bash
curl -sS --connect-timeout 5 --max-time 90 --retry 1 \
  "https://api.antalpha.com/agent-skills/v1/assets?input={{input}}" \
  -H "Accept: application/json"

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill asks to remember wallet addresses and supports persistent retrieval, but it does not clearly warn that the address will be stored and reused later. Wallet addresses are sensitive financial metadata; persistent storage without explicit privacy notice and retention expectations can expose user activity patterns or link identities across sessions.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The skill transmits the full canonical wallet address to a memory API for persistence. Even though the endpoint is local, this is still an external transmission of sensitive user financial identifiers, and compromise or logging at that service boundary could expose wallet ownership correlations.

Content

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

When the user clearly wants to save (e.g. "remember", "yes", "记住", "是", "save this address"), call with the full canonical address from the query you just ran:

bash
curl -sS --connect-timeout 5 --max-time 15 -X POST \
  "http://127.0.0.1:3000/agent-skills/v1/memory" \
  -H "Content-Type: application/json" \
  -d '{"add":"{{full canonical address or original input}}"}'

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest describes the skill as handling 'Multi-chain wallet balances (EVM + BTC)' with non-BTC addresses using the MCP wallet-balance-query tool, which implies an EVM-plus-BTC scope. However, the code explicitly detects and processes many additional non-EVM chains such as Tron, TON, XRP, Litecoin, NEAR, Sui, Aptos, Polkadot, Cardano, Kaspa, and Solana, expanding the supported asset-query scope beyond what the description claims.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill sends user-supplied wallet addresses to external services such as the MCP endpoint and public blockchain/RPC providers. Even if needed for functionality, transmitting wallet identifiers without clear disclosure or consent creates a privacy issue because third parties can correlate addresses, timing, and source metadata.

Content

No source excerpt is available for this finding.

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 · server.js (reported line 744)May include surrounding context.

js
}

async function fetchCoinPriceUsd(ids, signal) {
  const url = `https://api.coingecko.com/api/v3/simple/price?ids=${encodeURIComponent(ids.join(","))}&vs_currencies=usd`;
  const resp = await fetch(url, {
    method: "GET",
    headers: { Accept: "application/json" },

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The service persistently stores wallet addresses in a local file and exposes read/write memory endpoints, but there is no authentication, per-user isolation, consent tracking, or deletion policy. Wallet addresses are sensitive financial identifiers, so storing them globally can leak one user's remembered addresses to another caller and create privacy risk.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The manifest describes a skill focused on querying wallet balances and remembering addresses, but this lockfile shows first-party dependencies on Express and Redis in addition to chain-query libraries. While Redis may support the stated 'remembering addresses' feature, Express indicates a general HTTP server capability that is not part of the user-facing skill purpose as described in the manifest and expands the skill's operational surface beyond simple balance retrieval.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: body-parser==1.20.4 — 1 advisory(ies): CVE-2026-12590 (body-parser vulnerable to denial of service when invalid limit value silently di)

Low
Category
Supply Chain
Confidence
62% confidence
Finding

body-parser processes attacker-controlled HTTP request bodies, so a denial-of-service flaw in this dependency can be reachable in an Express-based service. In this wallet-balance skill, availability matters because the package set indicates a network-facing gateway, but the issue appears limited to service disruption rather than code execution or data compromise.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: qs==6.14.2 — 3 advisory(ies): CVE-2026-82417 (qs: Denial of Service via Attacker Controlled isBuffer); CVE-2026-8723 (qs has a remotely triggerable DoS: qs.stringify crashes with TypeError on null/u); CVE-2026-82562 (qs array-limit bypass via bracket-key comma parsing)

Low
Category
Supply Chain
Confidence
70% confidence
Finding

qs is commonly used to parse or serialize query strings from untrusted input, and the listed advisories are denial-of-service class issues. Because this skill likely exposes HTTP query endpoints for address lookups, malformed attacker-supplied parameters could potentially consume resources or crash request handling, though the impact is still primarily availability-related.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

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

json
"author": "Antalpha",
  "license": "MIT",
  "dependencies": {
    "decimal.js": "^10.5.0",
    "dotenv": "^17.3.1",
    "ethers": "^6.13.2",
    "express": "^4.21.2",

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

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

json
"license": "MIT",
  "dependencies": {
    "decimal.js": "^10.5.0",
    "dotenv": "^17.3.1",
    "ethers": "^6.13.2",
    "express": "^4.21.2",
    "helmet": "^8.1.0",

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

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

json
"dependencies": {
    "decimal.js": "^10.5.0",
    "dotenv": "^17.3.1",
    "ethers": "^6.13.2",
    "express": "^4.21.2",
    "helmet": "^8.1.0",
    "redis": "^4.7.0"

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

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

json
"decimal.js": "^10.5.0",
    "dotenv": "^17.3.1",
    "ethers": "^6.13.2",
    "express": "^4.21.2",
    "helmet": "^8.1.0",
    "redis": "^4.7.0"
  }

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

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

json
"dotenv": "^17.3.1",
    "ethers": "^6.13.2",
    "express": "^4.21.2",
    "helmet": "^8.1.0",
    "redis": "^4.7.0"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

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

json
"ethers": "^6.13.2",
    "express": "^4.21.2",
    "helmet": "^8.1.0",
    "redis": "^4.7.0"
  }
}

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The manifest repeatedly frames the feature around wallet addresses and remembering addresses, but the code also accepts ENS and .bnb domain names and resolves them to on-chain addresses. This is extra user-facing behavior not reflected in the declared description, creating a smaller but real description-to-behavior mismatch.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
server.js:13