Back to skill

Security audit

wallet-pnl

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to perform Solana wallet analysis, but it should be reviewed because it documents a mutable npx payment command and under-scoped paid/network behavior.

Review before installing or invoking the paid path. Do not run the documented npx awal@latest command in a privileged environment; prefer a pinned, reviewed payment client and confirm any paid request before sending a wallet address. Self-hosters should remove PaxHeader packaging artifacts, pin dependencies, restrict or validate FACILITATOR_URL, remove unused balance/token-account queries unless documented, and add rate limits or payment checks to /demo if production access should be paid.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/pnl.py:60
Finding
Unnecessary Retrieval of Wallet Balance and Token Holdings<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pnl.py:60-92`, `scripts/pnl.py:136-140` **Vulnerability Type**: Excessive data access and violation of least-privilege principles **Risk Level**: Medium ### Complete Code Snippet ```python def fetch_sol_balance(wallet: str) -> float: """Get current SOL balance via public RPC""" rpc_url = (f"https://mainnet.helius-rpc.com/?api-key={HELIUS_KEY}" if HELIUS_KEY else "https://api.mainnet-beta.solana.com") try: r = requests.post(rpc_url, json={ "jsonrpc": "2.0", "id": 1, "method": "getBalance", "params": [wallet] }, headers=HEADERS, timeout=8) if r.status_code == 200: res = r.json().get("result", {}) if isinstance(res, dict) and "value" in res: return res["value"] / 1e9 except Exception: pass return -1 def fetch_token_accounts(wallet: str) -> list: """Get current token holdings""" rpc_url = (f"https://mainnet.helius-rpc.com/?api-key={HELIUS_KEY}" if HELIUS_KEY else "https://api.mainnet-beta.solana.com") try: r = requests.post(rpc_url, json={ "jsonrpc": "2.0", "id": 1, "method": "getTokenAccountsByOwner", "params": [wallet, {"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}, {"encoding": "jsonParsed"}] }, headers=HEADERS, timeout=8) if r.status_code == 200: result = r.json().get("result", {}) return result.get("value", []) except Exception: pass return [] def analyze_wallet(wallet: str, tx_limit: int = 100) -> PnLResult: result = PnLResult(wallet=wallet) # Fetch data txns = fetch_helius_transactions(wallet, tx_limit) sol_balance = fetch_sol_balance(wallet) token_accounts = fetch_token_accounts(wallet) ``` ### Technical Analysis The declared functionality requires recent swap history ...[truncated 1828 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the unused calls from `analyze_wallet`: ```python txns = fetch_helius_transactions(wallet, tx_limit) ``` 2. Delete `fetch_sol_balance` and `fetch_token_accounts` if no documented feature requires them. 3. If portfolio information is introduced as a future feature, make retrieval explicit and opt-in. 4. Document which wallet information is sent to which provider and why. 5. Cache necessary public-chain queries and apply rate limits to reduce provider exposure and quota consumption. 6. Add tests asserting that a normal PnL analysis invokes only the RPC/API operations needed for swap-history analysis. ]]>

T08 · Insecure Dependencies

Error
Location
SKILL.md:27
Finding
Runtime Execution of an Unpinned npm Package<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:27` **Vulnerability Type**: Mutable third-party dependency execution **Risk Level**: High ### Complete Code Snippet ```bash npx awal@latest x402 pay "https://wallet-pnl-production.up.railway.app/pnl?wallet=WALLET_ADDRESS" ``` ### Technical Analysis The documented payment workflow instructs users to execute `awal@latest` through `npx`. If the package is not already available locally, `npx` may download it and execute its code, including applicable package lifecycle behavior. The `@latest` selector is mutable. Consequently, the package version executed by a user can differ from the version available when this Skill was reviewed. A compromised npm publisher account, malicious future release, or upstream package compromise could therefore convert the documented command into arbitrary local code execution. This command is not invoked automatically by the Python implementation; exploitation requires a user or agent to follow the instruction. Nevertheless, execution occurs with the privileges of that user or agent. ### Attack Path 1. An attacker compromises the `awal` npm package, its publisher account, or a dependency used by a future release. 2. The attacker publishes a malicious version and assigns it the npm `latest` tag. 3. A user follows the payment command from `SKILL.md`. 4. `npx` resolves `awal@latest` and downloads the malicious release. 5. Package code executes with the invoking user's local permissions. 6. The malicious package could access files, environment variables, network credentials, or other resources available to that user. ### Impact Assessment A successful supply-chain compromise could provide arbitrary code execution under the invoking account. The accessible scope would include any files, environment variables, network resources, and credentials readable or writable by that account. The repository itself contains no evidence that the current package is malicious, and the ...[truncated 173 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `@latest` with an exact, reviewed version: ```bash npx awal@X.Y.Z x402 pay "https://wallet-pnl-production.up.railway.app/pnl?wallet=WALLET_ADDRESS" ``` 2. Verify and document the package publisher, source repository, release provenance, and expected integrity. 3. Prefer a lockfile-backed installation with integrity metadata over ad hoc runtime resolution. 4. Where practical, provide a small reviewed payment client as part of the project rather than downloading executable code at invocation time. 5. Run payment tooling in a constrained environment with minimal filesystem and environment-variable access. 6. Review new package versions before updating the pinned version. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
api/server.py:102
Finding
Paid Analysis Can Be Accessed Through an Unauthenticated Free Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `api/server.py:102-114` **Vulnerability Type**: Payment-control bypass and unrestricted resource consumption **Risk Level**: Medium ### Complete Code Snippet ```python @app.get("/demo") async def demo(wallet: str, limit: int = 50): """Free endpoint for web UI""" if not wallet or len(wallet) < 32: raise HTTPException(400, "Invalid wallet address") if not os.environ.get("HELIUS_API_KEY"): raise HTTPException(503, "HELIUS_API_KEY required for transaction history") try: r = analyze_wallet(wallet, min(limit, 50)) return result_to_dict(r) except Exception as e: raise HTTPException(500, str(e)) ``` For comparison, payment enforcement is only applied to the `/pnl` endpoint: ```python @app.get("/pnl") async def pnl(wallet: str, request: Request, limit: int = 100): if not wallet or len(wallet) < 32: raise HTTPException(400, "Invalid wallet address") if PAY_TO: ph = request.headers.get("X-PAYMENT") if not ph: return Response(content=json.dumps(payment_requirements()), status_code=402, headers={"Content-Type": "application/json"}) if not verify_payment(request): raise HTTPException(402, "Invalid or expired payment") ``` ### Technical Analysis The service advertises `/pnl` as a paid x402 endpoint. However, `/demo` accepts an arbitrary wallet, calls the same `analyze_wallet` function, and returns the same result structure without payment, authentication, or rate limiting. The demo endpoint limits transaction analysis to 50 records instead of the paid endpoint's maximum of 200, but it still exposes the core analysis functionality. Because each request initiates external Helius/RPC operations, automated callers can also consume server resources and the operator's API quota without incurring payment. The source explicitly describes `/demo` as a free endpoint for the w ...[truncated 1314 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `/demo` from production if all arbitrary wallet analyses are intended to require payment. 2. Alternatively, apply the same x402 verification and settlement controls used by `/pnl`. 3. If a free demonstration is required, restrict it to one or more fixed demonstration wallets rather than accepting arbitrary input. 4. Add per-IP and global rate limits, request quotas, and abuse monitoring. 5. Cache results for repeated wallet queries to limit unnecessary Helius usage. 6. Separate demo and production deployments or disable the endpoint through a production configuration flag. 7. Return generic internal-error messages rather than exposing `str(e)` directly. 8. Add integration tests confirming that production analysis routes cannot invoke `analyze_wallet` without the intended payment or authorization policy. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (54)

Tainted flow: 'FACILITATOR' from os.environ.get (line 26, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
if not ph:
        return False
    try:
        r = requests.post(f"{FACILITATOR}/verify",
                          json={"payment": ph, "paymentRequirements": payment_requirements()["accepts"][0]},
                          timeout=10)
        return r.status_code == 200 and r.json().get("isValid", False)
Confidence
90% confidence
Finding
The code sends payment data to a facilitator URL fully controlled by the FACILITATOR_URL environment variable, with no allowlist, scheme validation, or authenticity checks. If an attacker can influence deployment configuration, they can redirect verification requests to an arbitrary host, exfiltrate payment tokens/metadata, and potentially spoof successful payment validation responses to bypass billing.

Tainted flow: 'FACILITATOR' from os.environ.get (line 26, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
def settle_payment(ph: str) -> dict:
    try:
        r = requests.post(f"{FACILITATOR}/settle",
                          json={"payment": ph, "paymentRequirements": payment_requirements()["accepts"][0]},
                          timeout=10)
        return r.json() if r.status_code == 200 else {}
Confidence
90% confidence
Finding
The settlement call also posts payment data to an environment-controlled external URL without trust validation. A malicious or misconfigured facilitator can receive payment artifacts, return crafted settlement data, or interfere with payment state, creating both data exposure and payment-integrity risks.

Tainted flow: 'rpc_url' from os.environ.get (line 80, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
rpc_url = (f"https://mainnet.helius-rpc.com/?api-key={HELIUS_KEY}"
               if HELIUS_KEY else "https://api.mainnet-beta.solana.com")
    try:
        r = requests.post(rpc_url, json={
            "jsonrpc": "2.0", "id": 1, "method": "getBalance", "params": [wallet]
        }, headers=HEADERS, timeout=8)
        if r.status_code == 200:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'rpc_url' from os.environ.get (line 80, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
rpc_url = (f"https://mainnet.helius-rpc.com/?api-key={HELIUS_KEY}"
               if HELIUS_KEY else "https://api.mainnet-beta.solana.com")
    try:
        r = requests.post(rpc_url, json={
            "jsonrpc": "2.0", "id": 1,
            "method": "getTokenAccountsByOwner",
            "params": [wallet,
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Ae3

High
Category
analysis-evasion
Confidence
90% confidence
Finding
Text artifact contains embedded NUL bytes

Ae3

High
Category
analysis-evasion
Confidence
90% confidence
Finding
The file appears to be a tar/PAX header artifact rather than a normal markdown skill file, and it contains embedded NUL/control bytes in extended attribute metadata. While this may be accidental packaging metadata, such bytes can confuse parsers, truncate text, or cause downstream tooling to misread or skip content during security review and skill loading.

Ae3

High
Category
analysis-evasion
Confidence
90% confidence
Finding
Text artifact contains embedded NUL bytes

Ae3

High
Category
analysis-evasion
Confidence
90% confidence
Finding
Text artifact contains embedded NUL bytes

Ae3

High
Category
analysis-evasion
Confidence
90% confidence
Finding
Text artifact contains embedded NUL bytes

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
There is a clear mismatch because the declared description promises a Solana wallet analytics capability, but the provided code chunk contains only file/archive metadata and no executable logic implementing those features. Since no behavior relevant to wallet analysis is visible, the actual supplied content does not substantiate the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description promises a wallet analytics capability focused on Solana trading performance. However, the provided code chunk contains only PaxHeader/archive metadata lines such as mtime and xattr provenance fields. There is no visible logic for blockchain access, wallet parsing, transaction analysis, PnL calculation, or trader evaluation. Because the actual supplied content does not implement the stated purpose and instead appears unrelated metadata, this is a clear description-behavior mismatch.

Ae3

High
Category
analysis-evasion
Confidence
90% confidence
Finding
Text artifact contains embedded NUL bytes

Ae3

High
Category
analysis-evasion
Confidence
90% confidence
Finding
Text artifact contains embedded NUL bytes

Ae3

High
Category
analysis-evasion
Confidence
90% confidence
Finding
Text artifact contains embedded NUL bytes

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding
Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding
Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Ae3

High
Category
analysis-evasion
Confidence
90% confidence
Finding
Text artifact contains embedded NUL bytes

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Ae4

Medium
Category
analysis-evasion
Confidence
81% confidence
Finding
The content shows suspicious nonstandard header/extended-attribute text with binary-looking characters, which is consistent with archive metadata leakage and possible encoding confusion. Even if not overtly malicious, mixed or malformed Unicode/binary content in a text artifact can interfere with review tools, diffing, and security scanners, making hidden content or parser discrepancies more likely.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill declares capabilities that imply environment-variable access and outbound network use, but it does not define an explicit tool scope such as permissions or allowed-tools. That creates an authorization gap where the runtime may permit broader behavior than users or reviewers expect, especially given the skill also references external API usage and a paid request flow.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The activation language is broad enough to trigger on many common wallet or trading-analysis prompts, increasing the chance the skill is invoked without clear user intent. In context, that is more concerning because the skill may make outbound calls and may incur paid usage, so overbroad routing can lead to unintended data disclosure or charges.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs sending wallet data to an external paid API but does not require an explicit user warning or confirmation about the outbound transmission and associated cost. This is dangerous because users may unknowingly disclose wallet addresses to a third party and trigger paid requests, which is especially sensitive in a financial-analysis context.

Static analysis

No suspicious patterns detected.