Back to skill

Security audit

Zapper Api

Security checks for vulnerabilities and agentic risk

Overview

The skill largely does what it says, but it can send all configured wallet addresses to Zapper by default and stores/uses an API key from a local plaintext config without enough disclosure or safeguards.

Review before installing if you maintain multiple pseudonymous wallets. Use explicit addresses or labels, avoid storing unrelated wallets together, prefer ZAPPER_API_KEY over a plaintext config file when practical, restrict config-file permissions if you use one, and remember that wallet addresses and query contents are sent to Zapper.

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)

other

Warning
Location
scripts/zapper.py:67
Finding

Implicit Disclosure and Correlation of All Configured Wallet Addresses

Content
View full analysis

Vulnerability Details

File Location: scripts/zapper.py:67-82, with affected command flows at scripts/zapper.py:614-668
Vulnerability Type: Excessive Wallet Data Disclosure
Risk Level: Medium

Vulnerable Code

python
def resolve_address(address_or_label):
    """Resolve address from argument or wallet label."""
    if address_or_label and address_or_label.startswith("0x"):
        return [address_or_label]

    wallets = get_wallets()
    if address_or_label:
        # Try to find by label
        for w in wallets:
            if w.get("label", "").lower() == address_or_label.lower():
                return [w["address"]]
        print(f"Error: Wallet '{address_or_label}' not found in config")
        sys.exit(1)

    if wallets:
        return [w["address"] for w in wallets]

The resulting address list is sent to the external Zapper API by multiple handlers. For example:

python
def cmd_tokens(args):
    addresses = resolve_address(args.address)
    data = fetch_tokens(addresses, args.limit)
    if args.json:
        print(json.dumps(data, indent=2))
    else:
        print(format_tokens(data, onchain=args.show_24h))

Technical Analysis

Commands accept the address argument as optional. If it is omitted and the configuration contains wallets, resolve_address() automatically returns every configured wallet address. The selected command then submits the entire list to https://public.zapper.xyz/graphql.

Transmitting a wallet address requested by the user is necessary for the declared portfolio-query functionality. Automatically transmitting all configured addresses is not necessary for an individual lookup and exceeds the minimum disclosure required. Although blockchain addresses are public, a collection of addresses associated with one local configuration is sensitive financial metadata. Sending them in one request reveals that they are likely controlled ...[truncated 1289 chars]

Remediation
View remediation

Remediation Suggestions

  • Require an explicit address or configured wallet label for all commands by default.
  • Add an explicit --all-wallets option for operations that intentionally query every configured wallet.
  • Display a clear notice or obtain confirmation before transmitting multiple configured addresses together.
  • Consider processing wallets in separate requests where aggregation is unnecessary, reducing direct correlation within a single request.
  • Update SKILL.md to document exactly when wallet addresses are transmitted, which endpoint receives them, and whether multiple addresses are submitted together.
  • Apply strict wallet-address validation before network transmission.

T09 · Insecure Skill Coding Practices

Note
Location
SKILL.md:15
Finding

Plaintext API Key Storage Without File-Permission Safeguards

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:15-25; configuration loading at scripts/zapper.py:16-33
Vulnerability Type: Insecure Plaintext Credential Storage
Risk Level: Low

Vulnerable Documentation and Code

The documented setup recommends placing the API key directly in a JSON file:

markdown
2. Configure in `~/.config/zapper/addresses.json`:
   ```json
   {
     "apiKey": "your-api-key",
     "wallets": [
       {"label": "Main", "address": "0x..."},
       {"label": "DeFi", "address": "0x..."}
     ]
   }
   ```

Or set environment variable: `export ZAPPER_API_KEY="your-api-key"`

The implementation reads the plaintext key without checking file ownership or permissions:

python
CONFIG_FILE = os.path.expanduser("~/.config/zapper/addresses.json")

def get_config():
    try:
        with open(CONFIG_FILE) as f:
            return json.load(f)
    except:
        return {"wallets": [], "apiKey": None}

def get_api_key():
    return os.environ.get("ZAPPER_API_KEY") or get_config().get("apiKey")

Technical Analysis

The API key is a reusable credential sent in the x-zapper-api-key request header. The setup documentation permits storing it in a regular plaintext file but does not instruct users to restrict the file to the owning account. The runtime also does not verify the configuration file's ownership or permission mode before reading and using the credential.

If the file is created with permissive permissions due to the user's umask, manual setup process, backup tooling, or configuration synchronization, another local account or process may be able to read the key. Plaintext storage is not inherently avoidable for every CLI, but the absence of permission guidance and runtime checks weakens credential protection.

Attack Path

  1. A user follows the documented setup and creates ~/.config/zapper/addresses.json containing the API key.
  2. The ...[truncated 757 chars]
Remediation
View remediation

Remediation Suggestions

  • Prefer an operating-system credential store or another dedicated secret-management facility.
  • If file-based storage remains supported, require the configuration directory to be accessible only by the user and the file to use mode 0600.
  • Add setup instructions such as chmod 600 ~/.config/zapper/addresses.json.
  • At runtime, inspect file ownership and permission bits before loading a stored key.
  • Refuse to use the key, or emit a prominent warning, when the file is group-readable, world-readable, or owned by another account.
  • Keep the environment-variable option, while documenting that process environments can also be visible in some execution and diagnostic contexts.
  • Avoid including the API key in logs, exceptions, JSON output, or configuration display commands.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (6)

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

Setup

  1. Get API key from Zapper Dashboard (free tier available)
  2. Configure in ~/.config/zapper/addresses.json:
    json
    {
    

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill invokes capabilities that rely on environment variables and outbound network access, but it does not declare an explicit tool scope or permissions boundary. This weakens least-privilege controls and can lead to overbroad execution in environments where undeclared network or env access should be restricted or reviewed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation encourages sending wallet addresses, portfolio composition, NFTs, and transaction history to a third-party API without any privacy warning or data-handling caveat. While wallet addresses are public on-chain, aggregating them with balances, holdings, and time-bounded transaction history materially increases user privacy risk and may expose sensitive financial profiling data.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
84% confidence
Finding

The curl examples show direct transmission of wallet addresses and authenticated API requests to an external service, which is a genuine external data flow. In this skill context the transmission is expected functionality, but it still creates privacy and secret-handling risk if users or implementers do not realize addresses and API keys are being sent to a third party.

Content

Scanner excerpt · references/API.md (reported line 183)May include surrounding context.

}

text

## curl Examples

### Portfolio
```bash

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The code sends wallet addresses and, via other query functions, transaction-history requests to Zapper's third-party API without any explicit consent flow, privacy warning, or data-minimization control. Wallet addresses are sensitive identifiers in a DeFi context because they enable profiling of balances, positions, NFTs, and historical activity, so users may unknowingly disclose financial metadata to an external service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill sends wallet addresses and associated portfolio, NFT, and transaction queries to a third-party API, but it does not clearly warn users that this financial activity data leaves the local environment. While wallet addresses are public on-chain, correlating them with labels like "Main" or "DeFi" can expose sensitive behavioral and identity-linked information.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.