Back to skill

Security audit

Banana Farmer

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed finance-data skill that uses a Banana Farmer API key to fetch market signals and does not show hidden persistence, local modification, trading, or credential theft.

Install only if you are comfortable giving Banana Farmer an API key and having queried tickers, including portfolio holding symbols, sent to bananafarmer.app. Treat the results as research data rather than financial advice, and review the provider's terms and privacy policy before using the trial-key signup flow.

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
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (23)

Tainted flow: 'req' from os.environ.get (line 58, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, headers=headers)
    ctx = ssl.create_default_context()
    try:
        with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
            body = json.loads(resp.read().decode('utf-8'))
            return body.get('data', body)
    except urllib.error.HTTPError as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 20, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, headers=headers)
    ctx = ssl.create_default_context()
    try:
        with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
            body = json.loads(resp.read().decode('utf-8'))
            return body.get('data', body)
    except urllib.error.HTTPError as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 20, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, headers=headers)
    ctx = ssl.create_default_context()
    try:
        with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
            body = json.loads(resp.read().decode('utf-8'))
            return body.get('data', body)
    except urllib.error.HTTPError as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 20, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, headers=headers)
    ctx = ssl.create_default_context()
    try:
        with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
            body = json.loads(resp.read().decode('utf-8'))
            return body.get('data', body)
    except urllib.error.HTTPError as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 20, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, headers=headers)
    ctx = ssl.create_default_context()
    try:
        with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
            body = json.loads(resp.read().decode('utf-8'))
            return body.get('data', body)
    except urllib.error.HTTPError as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 20, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, headers=headers)
    ctx = ssl.create_default_context()
    try:
        with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
            body = json.loads(resp.read().decode('utf-8'))
            return body.get('data', body)
    except urllib.error.HTTPError as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 20, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, headers=headers)
    ctx = ssl.create_default_context()
    try:
        with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
            body = json.loads(resp.read().decode('utf-8'))
            return body.get('data', body)
    except urllib.error.HTTPError as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The code is clearly finance-related and does implement part of the declared domain: it fetches momentum-related signal data for tickers and reports score, RSI, coil score, price, volatility, and simple risk indicators. However, the declared description presents a much broader product: top-signal scanning across thousands of assets, portfolio monitoring with alerts, market/sector intelligence, AI analysis, and backtested win-rate evidence. None of those broader capabilities appear in this code chunk. Instead, this specific script's actual purpose is a narrow CLI comparison tool for two or more specified tickers using one API family. Because the implemented primary behavior is materially narrower and several prominent declared capabilities are absent, this is a description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code’s actual function is narrow: it fetches and formats signal data for explicitly provided ticker symbols using a single API endpoint. This aligns only with the 'look up any ticker for momentum scores, RSI, coil breakout patterns, and AI analysis' portion of the description. However, the declared purpose prominently claims additional capabilities—market-wide scanning, portfolio tracking, alerts, sector/market intelligence, and broader conversational portfolio support—that are absent from the provided code chunk. There are no undeclared sensitive behaviors, but there is a material description-to-behavior mismatch because the implementation shown supports only a subset of the advertised functionality.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The code only exposes three read-only API-backed commands focused on system health, top signals, and market pulse/trending data. While this partially overlaps with the description's 'market pulse' and 'top signals' claims, the description substantially overstates the skill's functionality. There is no implementation for looking up arbitrary tickers, calculating or displaying RSI or coil breakout patterns, performing AI analysis, scanning a large universe beyond whatever the API returns, tracking portfolios, issuing alerts, or presenting backtest/win-rate/risk data. The primary purpose in code is a narrow Banana Farmer market-status and top-signals CLI, not the full portfolio-intelligence assistant described.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a comprehensive conversational market intelligence and portfolio assistant. The supplied code does not implement that scope. It only fetches a single Banana Farmer endpoint, signals/movers, and displays winners/losers for a recent period with basic formatting and a simple percentage of winners among returned movers. This is materially narrower than the declared purpose and lacks most of the advertised capabilities such as ticker-specific analytics, RSI, breakout detection, AI commentary, portfolio monitoring, alerts, sector/risk views, or broad scanning/backtest logic. Therefore the description does not accurately represent what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The code chunk’s actual function is limited to sector-level analysis of a top-signals feed from the BananaFarmer API. It fetches `signals/top?limit=50&badge=all`, classifies symbols into sectors using a hardcoded map plus name heuristics, and outputs ranked sector momentum statistics. While the declared description mentions sector trends and top-signal scanning in general, it presents a much broader skill centered on ticker lookup, momentum/RSI/breakout analytics, AI analysis, portfolio monitoring, alerts, crypto coverage, market pulse, and backtested proof/risk features. None of those broader capabilities appear in this code. Because the code’s primary behavior is materially narrower and different from the declared full-featured portfolio intelligence skill, this is a description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code’s actual scope is much narrower than the declared description. It is a simple CLI wrapper around three Banana Farmer endpoints: content/watchlist, content/scorecard, and stats/horizons. Those endpoints can surface curated picks and performance statistics, which partially overlaps with the claims about watchlists, RSI/coil data, and backtested win rates. However, the declared purpose emphasizes a comprehensive scanner and portfolio intelligence product with arbitrary ticker lookup, top-signal scanning across thousands of assets, portfolio monitoring, alerts, market/sector/risk analysis, and natural-language interaction. None of those broader capabilities are implemented in this code chunk. Therefore the description materially overstates and misrepresents what this specific supplied code actually does.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill declares access to environment variables and outbound network use but does not define any explicit tool scope or allowed-tools boundary. In an agent ecosystem, missing scope declarations can cause over-broad execution authority, making it easier for a skill to access secrets like BF_API_KEY or perform network actions outside what the user expects.

External Transmission

Medium
Category
Data Exfiltration
Content
**Option A — Self-provision a free key instantly (no account needed):**
```bash
curl -s -X POST "https://bananafarmer.app/api/bot/v1/keys/trial" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Agent", "email": "you@example.com"}'
```
Confidence
88% confidence
Finding
The skill instructs users to send identifying information, specifically name and email, to an external service in order to provision a key. Any externally transmitted personal data introduces privacy and supply-chain risk, and because this happens before the user can independently assess the service, it increases exposure to unwanted tracking or data collection.

Vague Triggers

Medium
Confidence
88% confidence
Finding
This markdown file lists activation-style example prompts such as "Check my portfolio" and "How are my holdings doing?" without any explicit scoping constraints or negative examples. Those phrases are common natural language requests and may cause unintended invocation outside a clearly bounded skill context.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The manifest describes a broad financial-intelligence skill with per-ticker analysis, portfolio monitoring, alerts, and multiple analytics domains. In this file, the implemented behavior is limited to three read-only commands that fetch market health, top signals, and a general pulse feed; none of the advertised portfolio, alerting, lookup, or risk-analysis capabilities are present here.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The script reads a local portfolio file and uses holding symbols to make external API calls to bananafarmer.app, which is a network operation involving user financial data. Although the API key requirement is disclosed, there is no user-facing notice that portfolio-derived data will be sent to a third-party service.

Vague Triggers

Low
Confidence
84% confidence
Finding
Prompts like "What's the market doing today?" and "What's trending in the market?" are broad, everyday questions that could match many unrelated finance or news contexts. The file does not provide exclusion conditions or a precise scope for when these phrases should invoke this specific skill.

Vague Triggers

Low
Confidence
82% confidence
Finding
Examples like "Check system health" and "Any data issues right now?" are ambiguous and not clearly limited to the Banana Farmer market-data service. Without context restrictions, these phrases could unintentionally overlap with unrelated diagnostics or support tasks.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The script silently sends BF_API_KEY to a third-party service on every request without any user-facing notice or consent mechanism. In an agent-skill context, hidden outbound authenticated calls can matter because users may not realize the tool is using a stored secret and contacting an external service, which reduces transparency and can create trust and privacy concerns even though the endpoint is hardcoded and appears legitimate.

Missing User Warnings

Low
Confidence
85% confidence
Finding
This code reads BF_API_KEY from the environment and sends it to a remote service via the x-bf-bot-key header. Although the operation is part of the script's purpose, the file itself does not disclose that it will transmit credentials to an external API, which is the kind of network/credential use SQP-2 asks to flag when no warning is present in code.

Intent-Code Divergence

Low
Confidence
89% confidence
Finding
In `show_scorecard`, the comment at L125 states the API returns percentage values as whole percentages (for example, 76.5 means 76.5%), but `show_horizons` formats `winRate` and `avgReturn` with `%` style multipliers (`:.1%`, `:.2%`). If the horizons endpoint uses the same convention, the displayed values would be off by 100x, contradicting the documented interpretation of API data.

Static analysis

No suspicious patterns detected.