Back to skill

Security audit

Wallet Balance

Security checks for vulnerabilities and agentic risk

Overview

This wallet-balance skill mostly matches its stated purpose, but its saved-address memory can be read or changed through unauthenticated server endpoints if the service is reachable.

Review before installing, especially if you might run the gateway on a shared machine, container, or public network. Bind it to localhost, firewall port 3000, add authentication or disable the memory endpoints, avoid saving wallet addresses unless needed, and update the reported vulnerable dependencies before production use.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (3)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:18
Finding

Mandatory Branded Response Injection

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server.js:996
Finding

Unauthenticated Global Wallet-Memory Disclosure and Modification

Content
View full analysis
String(a || "").trim()).filter(Boolean))]; } if (Array.isArray(j?.items)) { const xs = j.items.map((it) => String(it?.address || it || "").trim()).filter(Boolean); return [...new Set(xs)]; } } catch { // missing or invalid file } return []; } async function writeMemoryAddresses(addresses) { const unique = [...new Set(addresses.map((a) => String(a || "").trim()).filter(Boolean))]; const dir = path.dirname(MEMORY_FILE); await fs.mkdir(dir, { recursive: true }); const tmp = `${MEMORY_FILE}.${process.pid}.tmp`; await fs.writeFile(tmp, JSON.stringify({ version: 1, addresses: unique }, null, 2), "utf8"); await fs.rename(tmp, MEMORY_FILE); return unique; } ``` The API exposes the complete list without authentication: ```js app.get("/agent-skills/v1/memory", async (_req, res) => { 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)); } }); ``` The same unauthenticated API permits additions and removals: ```js app.post("/agent-skills/v1/memory", async (req, res) => { const requestId = m ...[truncated 3930 chars]
Remediation
View remediation
{ console.log(`[server] listening on 127.0.0.1:${PORT}`); }); ``` 2. Require authenticated requests for all memory endpoints and for `from_memory=1`. 3. Associate every stored address with an authenticated user, agent, or tenant identifier. 4. Enforce authorization checks before listing, adding, removing, or querying addresses. 5. Do not return complete address lists unless the authenticated caller explicitly requires them; return redacted values where possible. 6. Store separate memory files or database records per principal rather than using one global list. 7. Create memory files with restrictive permissions, such as mode `0600`, and secure the containing directory. 8. Add CSRF protection or reject browser-originated requests if cookie-based authentication is introduced. 9. Document that public deployment requires TLS, authentication, firewall restrictions, and explicit reverse-proxy configuration. 10. Add integration tests proving that one user cannot read or alter another user's remembered addresses. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
server.js:330
Finding

Spoofable and Fail-Open Request Rate Limiting

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; } } ``` The trusted request address is then used as the rate-limit identity: ```js const ip = req.ip || req.headers["x-forwarded-for"] || "unknown"; const bypassCache = String(req.query.refresh || req.query.nocache || "").toLowerCase() === "1" || String(req.query.refresh || "").toLowerCase() === "true"; ``` Redis failure is tolerated while the server continues accepting requests: ```js async function bootstrap() { 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` causes proxy forwarding headers to influence `req.ip`. If the serv ...[truncated 2251 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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (24)

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

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The documented purpose understates important behaviors: persistent storage of wallet addresses, broader chain coverage, and additional backend components. This can mislead users and reviewers about what data is stored and where requests may go, undermining informed consent and security review.

Content

No source excerpt is available for this finding.

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
88% confidence
Finding

ws 8.17.1 is flagged for memory disclosure and memory-exhaustion issues, which are more serious than the other findings because WebSocket traffic is long-lived and attacker-controlled. Even though this file alone does not prove WebSockets are enabled, the presence of ethers means ws may be used for RPC/WebSocket providers, and if exposed in this wallet balance service it could permit information leakage or service disruption.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The README describes automatic use of Antalpha MCP and public providers for EVM/BTC queries, which means wallet addresses are transmitted to third-party services, but it does not explicitly warn users about that disclosure. Wallet addresses may be linkable identifiers, so silent transmission can expose user financial metadata to external operators and logs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README states that addresses can be remembered and later queried automatically, but it does not warn users that wallet addresses are persistently stored and may be sensitive metadata. Even though addresses are public on-chain, persistent local storage tied to a user can reveal holdings, behavioral patterns, and wallet ownership relationships.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The README advertises broad natural-language triggers like "What's in my wallet?" and "查我的余额" without defining explicit activation boundaries or confirmation requirements. In an agent setting, this can cause unintended invocation from ordinary conversation and trigger balance lookups against remembered addresses, creating avoidable privacy exposure and unexpected third-party network requests.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill declares network and likely persistence-related behavior but does not declare an explicit tool/permission scope. That weakens policy enforcement and user/host visibility into what the skill can access, making unintended network calls or data handling harder to constrain or audit.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The remembered-address trigger is broad enough that common phrases like 'check my balance' can automatically query previously stored wallet addresses without a fresh address or an explicit reminder to the user. In context, wallet addresses and portfolio balances are sensitive financial data, so overbroad triggers increase the chance of unintended disclosure.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

The skill sends user-supplied wallet identifiers to an external public deployment at api.antalpha.com when local handling fails. Wallet addresses are sensitive metadata because they can be linked to financial holdings and activity, so fallback transmission to a third party increases privacy and compliance risk.

Content

Scanner excerpt · SKILL.md (reported line 75)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
94% confidence
Finding

The skill supports saving wallet addresses but does not clearly warn users up front that addresses will be persistently stored. Since wallet addresses can reveal financial activity and identity links, saving them without prominent disclosure and consent creates a privacy risk.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
82% confidence
Finding

This flow transmits the user's full canonical wallet address to a memory endpoint for persistent storage. Even though the destination is local, it is still a data transfer of sensitive financial identifiers, and if the local service is compromised or multi-tenant this can expose user portfolio associations.

Content

Scanner excerpt · SKILL.md (reported line 103)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 narrows scope to 'Multi-chain wallet balances (EVM + BTC)' and specifically distinguishes supported non-BTC addresses via the MCP wallet-balance-query tool, which implies non-BTC support is limited to the stated EVM set plus BTC. In code, detectNonEvmChain and normalizeInput explicitly recognize and process many other chains such as Solana, Tron, TON, XRP, Litecoin, NEAR, Sui, Aptos, Polkadot, Cardano, and Kaspa, expanding the skill beyond the manifest's described scope.

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
91% confidence
Finding

The memory endpoint persists wallet addresses to disk with no authentication, authorization, or in-band confirmation mechanism in this file. Because wallet addresses are sensitive financial metadata, an external caller can add, remove, or enumerate remembered addresses, causing privacy leakage and tampering with future balance queries.

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
82% confidence
Finding

The lockfile pins body-parser 1.20.4, which the scanner reports as affected by a denial-of-service issue around invalid limit handling. In an Express-based HTTP service, body-parser is directly exposed to untrusted request input, so a parser-level DoS is plausibly reachable if request size limits are configurable or malformed inputs are accepted.

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
76% confidence
Finding

qs 6.14.2 is reported as vulnerable to several denial-of-service conditions involving attacker-controlled parsing and stringify edge cases. Because qs is used by Express/body-parser for querystring and form parsing, remotely supplied parameters may be able to trigger excessive processing or crashes, though the impact is generally availability rather than code execution.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency uses a caret range, which permits automatic installation of newer compatible releases. While common in Node.js projects, this weakens supply-chain determinism and can expose deployments to malicious or vulnerable upstream releases without a code change in this repository.

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
95% confidence
Finding

The dotenv package is specified with a caret range, allowing newer minor/patch versions to be resolved over time. In a production skill handling wallet-related queries and saved addresses, this increases supply-chain risk because runtime behavior can change or a compromised upstream release could be pulled in implicitly.

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
95% confidence
Finding

The ethers dependency is not fully pinned, so future installs may consume newer releases within the allowed semver range. Because this skill processes blockchain-related data, unintended library changes could affect balance parsing, network behavior, or introduce a supply-chain compromise path.

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
95% confidence
Finding

Express is declared with a floating caret version, which can silently pull in newer releases during reinstall or deployment. Since Express is part of the server-facing attack surface, unreviewed dependency changes can increase exposure to newly introduced flaws or malicious supply-chain events.

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
95% confidence
Finding

Helmet is also specified with a caret range, which allows dependency drift between builds. Although Helmet is a defensive package, relying on non-exact versions still creates supply-chain uncertainty and may unexpectedly alter security header behavior.

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
95% confidence
Finding

Redis is declared with a non-exact version range, so installations may resolve to different package contents over time. In a skill that stores remembered wallet addresses, this adds supply-chain and reliability risk around a component likely involved in persistence and state handling.

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
95% confidence
Finding

The manifest includes a behavioral requirement that responses match the user's language. The implementation hardcodes all user-facing success and error messages in English and contains no language detection, locale selection, or translation path, so the documented behavior is not actually implemented here.

Content

No source excerpt is available for this finding.