Back to skill

Security audit

Bitcoin

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent read-only Bitcoin lookup skill, but it needs review because it can expose query metadata and an optional CoinGecko API key through under-scoped network behavior.

Review before installing if you care about wallet-query privacy or use a CoinGecko API key. Avoid setting COINGECKO_API_KEY for this skill until key handling is fixed, and only use a trusted HTTPS BITCOIN_API_URL or the default endpoint.

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

Warning
Location
scripts/bitcoin_client.py:91
Finding

CoinGecko API Key Exposed in URL Query String

Content
View full analysis
Remediation
View remediation
Optional[Any]: request_headers = { "Accept": "application/json", "User-Agent": "HermesAgent/1.0", } if headers: request_headers.update(headers) req = urllib.request.Request(url, headers=request_headers) # Existing request and retry handling follows. def _get_price_usd(currency: str = "USD") -> Optional[float]: currency_lower = currency.lower() cg_key = os.getenv("COINGECKO_API_KEY", "") url = ( "https://api.coingecko.com/api/v3/simple/price" f"?ids=bitcoin&vs_currencies={currency_lower}" ) auth_headers = {"x-cg-pro-api-key": cg_key} if cg_key else {} data = _http_get_json(url, timeout=10, retries=1, headers=auth_headers) ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/bitcoin_client.py:51
Finding

Unvalidated Custom API Endpoint Permits Cleartext Query Disclosure

Content
View full analysis
str: """Get Mempool.space API base URL, with optional override from environment.""" # Inline: load API URL at point of use, never extract to module-level try: from dotenv import dotenv_values env_data = dotenv_values() api_url = env_data.get("BITCOIN_API_URL") or MEMPOOL_API_DEFAULT except Exception: api_url = MEMPOOL_API_DEFAULT return api_url ``` The unvalidated value is subsequently used to construct requests containing user-supplied Bitcoin identifiers: ```python api_url = _get_mempool_api_url() url = f"{api_url}/address/{address}" data = _http_get_json(url) ``` ```python api_url = _get_mempool_api_url() url = f"{api_url}/tx/{txid}" tx = _http_get_json(url) ``` ### Technical Analysis `BITCOIN_API_URL` is accepted without validating its scheme, hostname, port, path, embedded credentials, or destination. In particular, an `http://` URL is permitted. Address and transaction queries sent over HTTP can be observed or modified by a network attacker. Bitcoin addresses and transaction IDs are public blockchain values, but the association between a particular user or system and the records being investigated can be privacy-sensitive. Query disclosure can reveal wallet interests, monitoring targets, business relationships, or financial activity. A custom endpoint is a legitimate feature because the Skill declares support for self-hosted Mempool instances. However, unrestricted destinations and cleartext transport are not necessary for the normal functionality. HTTPS should be required by default, with narrowly scoped handling for explicitly trusted local instances. Because the endpoint controls all return ...[truncated 1806 chars]
Remediation
View remediation
str: api_url = os.getenv("BITCOIN_API_URL", MEMPOOL_API_DEFAULT).rstrip("/") parsed = urlparse(api_url) if parsed.scheme != "https": raise ValueError("BITCOIN_API_URL must use HTTPS") if not parsed.hostname: raise ValueError("BITCOIN_API_URL must contain a valid hostname") if parsed.username or parsed.password: raise ValueError("Embedded URL credentials are not permitted") if parsed.query or parsed.fragment: raise ValueError("API base URL must not contain a query or fragment") return api_url ``` If loopback HTTP is operationally necessary, implement a separate option such as `BITCOIN_ALLOW_INSECURE_LOCAL_HTTP=1` and permit it only for validated loopback destinations. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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 · README.md (reported line 226)May include surrounding context.

md
### Q: Can this help me trade Bitcoin?

**A:** It provides data for context, but doesn't execute trades. To trade, you'd need to use an exchange API (Coinbase, Kraken, Binance) with write permissions, which is a separate integration. This skill is read-only.

### Q: Why are fee estimates different from what I see elsewhere?

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill documentation instructs execution of a local Python helper that uses both environment-controlled configuration (BITCOIN_API_URL) and outbound network access, but the skill declares no explicit tool scope such as permissions or allowed-tools. In agent environments, missing scope declarations can cause overbroad execution privileges or make network/env access invisible to policy enforcement, increasing the chance of unintended data exposure or unsafe remote interaction.

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 · scripts/bitcoin_client.py (reported line 99)May include surrounding context.

python
except Exception:
        cg_key = ""
    
    url = f"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies={currency_lower}"
    
    # Inline: use key immediately if available
    if cg_key:

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The README prominently states the skill is read-only and needs no API keys, but it does not clearly warn users up front that address, transaction, and balance lookups are sent to third-party services such as Mempool.space and CoinGecko. That omission can mislead privacy-conscious users into exposing their interest in specific addresses or transactions, creating metadata leakage even though no funds can be moved.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The address command sends the user-supplied Bitcoin address directly to the configured Mempool API endpoint, which discloses potentially sensitive wallet identifiers to a third-party service. In a blockchain-analysis skill this is expected functionality, but the lack of an explicit privacy warning means users may unknowingly reveal addresses they consider private or linkable.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.