Back to skill

Security audit

FinClaw

Security checks for vulnerabilities and agentic risk

Overview

FinClaw is a coherent finance assistant skill, but users should be careful because it installs Python dependencies, calls external market-data APIs, and mutates a local finance database.

Install only if you are comfortable running a local Python finance utility that contacts third-party market-data services and stores portfolio data locally. Review the unpinned dependencies before setup, avoid putting sensitive notes in the portfolio database on shared machines, and use remove/delete commands carefully because some local records are deleted without confirmation.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Unpinned Dependencies Permit Unreviewed Supply-Chain Code Execution

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
lib/db.py:8
Finding

Sensitive Financial Database Is Created Without Explicit Restrictive Permissions

Content
View full analysis
sqlite3.Connection: """Get a SQLite connection with WAL mode and foreign keys enabled.""" db_path = get_db_path() os.makedirs(os.path.dirname(db_path), exist_ok=True) conn = sqlite3.connect(db_path) conn.execute("PRAGMA journal_mode=WAL") conn.execute("PRAGMA foreign_keys=ON") conn.row_factory = sqlite3.Row ``` The database stores financial and user-generated information, including the following fields: ```sql CREATE TABLE IF NOT EXISTS transactions ( id INTEGER PRIMARY KEY AUTOINCREMENT, position_id INTEGER NOT NULL, symbol TEXT NOT NULL, action TEXT NOT NULL CHECK(action IN ('buy','sell')), shares REAL NOT NULL, price REAL NOT NULL, fees REAL DEFAULT 0, transaction_date DATE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (position_id) REFERENCES positions(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS alerts ( id INTEGER PRIMARY KEY AUTOINCREMENT, symbol TEXT NOT NULL, asset_type TEXT NOT NULL, condition TEXT NOT NULL CHECK(condition IN ('above','below','change_pct','volume_above')), target_value REAL NOT NULL, current_value_at_creation REAL, status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','triggered','snoozed','deleted')), triggered_at TIMESTAMP, snoozed_until TIMESTAMP, note TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### Technical Analysis The database directory and SQLite files are created using operating-system defaults controlled by the process umask. The code does not explicitly enforce owner-only permissions on the directory, primary database, write-ahead log, or shared-memory file. Under a permissive umask, oth ...[truncated 1332 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (40)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description presents a comprehensive multi-asset AI finance assistant with many analytics and tracking features. The supplied code only implements crypto-related data retrieval: Binance 24h ticker/price data, top USDT gainers, and CoinGecko TRY/USD price lookup for a limited coin map. There is no evidence of AI assistant behavior, charting, technical analysis, portfolio/watchlist management, alerts, daily briefings, macroeconomic content, sentiment analysis, or support for stocks/BIST/forex. The external resources accessed (Binance and CoinGecko) are consistent with a crypto subset of the description, but the overall declared scope is materially broader than the actual code, so this is a mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a full-featured AI finance assistant spanning multiple asset classes and analytical functions. The supplied code only implements foreign exchange rate retrieval and currency conversion from external exchange-rate APIs. While forex is one subset of the declared domain, the actual code lacks the vast majority of the described capabilities and has a much narrower primary purpose. This is therefore a material description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description presents a broad finance assistant focused on quotes, charts, technical analysis, portfolio features, alerts, watchlists, macroeconomics, and sentiment analysis. The supplied code chunk does not implement those primary capabilities; instead, it is a narrow news-fetching script that retrieves company and market news from Finnhub and formats the results. While 'daily briefings' could loosely relate to news, the code is specifically a news client and that capability is not clearly declared. It also uses external API/network access and an API key, which are not reflected in the declared permissions. Therefore, the code's actual behavior is materially narrower and partially different from the stated description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The supplied code chunk is narrowly focused on CRUD-style portfolio management and transaction recording. It detects asset type, assigns currency, stores buys and sells in a database, deletes positions, and prints portfolio listings and summaries. While 'portfolio tracking' is one item mentioned in the declared description, the overall declared purpose is a broad AI finance assistant centered on real-time data and analysis. This code instead implements state-changing portfolio bookkeeping with persistent database access, which is a materially different and more specific behavior than the description suggests. The missing declared capabilities and the undeclared transaction-management/database-writing behavior make this a clear description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The declared description promises a broad, feature-rich finance assistant spanning many capabilities and asset classes. The supplied code chunk only implements a narrow screener for top/bottom movers using current price versus previous close, plus a crypto top gainers path. While this is finance-related and overlaps slightly with quotes/screening for US, BIST, and crypto, it does not substantiate most of the declared capabilities and omits forex entirely. The mismatch is primarily one of materially overstated purpose and feature set, not malicious or undeclared sensitive behavior.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill instructs the agent to run local Python scripts and access external market/news APIs, which implies shell execution and network access, yet it declares no explicit tool scope or allowed-tools. Missing capability declarations weaken the trust boundary and can let a host agent invoke more privilege than users or reviewers expect.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill exposes portfolio and alert commands that can add, sell, remove, delete, and snooze records, but it does not prominently warn that these operations persist and mutate local state. In an agent setting, users may expect informational finance queries, so undisclosed state-changing behavior increases the risk of unintended data modification or loss.

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 · references/api-endpoints.md (reported line 10)May include surrounding context.

md
if not pair.endswith("USDT") and not pair.endswith("BUSD"):
        pair = pair + "USDT"
    wait_if_needed("binance")
    d = requests.get("https://api.binance.com/api/v3/ticker/24hr",
                     params={"symbol": pair}, timeout=10).json()
    return {"symbol": symbol.upper(), "pair": pair, "price": float(d["lastPrice"]),
            "change": float(d["priceChange"]), "change_pct": float(d["priceChangePercent"]),

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 · references/api-endpoints.md (reported line 11)May include surrounding context.

md
if not pair.endswith("USDT") and not pair.endswith("BUSD"):
        pair = pair + "USDT"
    wait_if_needed("binance")
    d = requests.get("https://api.binance.com/api/v3/ticker/24hr",
                     params={"symbol": pair}, timeout=10).json()
    return {"symbol": symbol.upper(), "pair": pair, "price": float(d["lastPrice"]),
            "change": float(d["priceChange"]), "change_pct": float(d["priceChangePercent"]),

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 · references/api-endpoints.md (reported line 12)May include surrounding context.

md
if not pair.endswith("USDT") and not pair.endswith("BUSD"):
        pair = pair + "USDT"
    wait_if_needed("binance")
    d = requests.get("https://api.binance.com/api/v3/ticker/24hr",
                     params={"symbol": pair}, timeout=10).json()
    return {"symbol": symbol.upper(), "pair": pair, "price": float(d["lastPrice"]),
            "change": float(d["priceChange"]), "change_pct": float(d["priceChangePercent"]),

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/check_alerts.py (reported line 17)May include surrounding context.

python
if not pair.endswith("USDT") and not pair.endswith("BUSD"):
        pair = pair + "USDT"
    wait_if_needed("binance")
    d = requests.get("https://api.binance.com/api/v3/ticker/24hr",
                     params={"symbol": pair}, timeout=10).json()
    return {"symbol": symbol.upper(), "pair": pair, "price": float(d["lastPrice"]),
            "change": float(d["priceChange"]), "change_pct": float(d["priceChangePercent"]),

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/crypto.py (reported line 17)May include surrounding context.

python
if not pair.endswith("USDT") and not pair.endswith("BUSD"):
        pair = pair + "USDT"
    wait_if_needed("binance")
    d = requests.get("https://api.binance.com/api/v3/ticker/24hr",
                     params={"symbol": pair}, timeout=10).json()
    return {"symbol": symbol.upper(), "pair": pair, "price": float(d["lastPrice"]),
            "change": float(d["priceChange"]), "change_pct": float(d["priceChangePercent"]),

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/crypto.py (reported line 28)May include surrounding context.

python
if not pair.endswith("USDT") and not pair.endswith("BUSD"):
        pair = pair + "USDT"
    wait_if_needed("binance")
    d = requests.get("https://api.binance.com/api/v3/ticker/24hr",
                     params={"symbol": pair}, timeout=10).json()
    return {"symbol": symbol.upper(), "pair": pair, "price": float(d["lastPrice"]),
            "change": float(d["priceChange"]), "change_pct": float(d["priceChangePercent"]),

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

python
if not pair.endswith("USDT") and not pair.endswith("BUSD"):
        pair = pair + "USDT"
    wait_if_needed("binance")
    d = requests.get("https://api.binance.com/api/v3/ticker/24hr",
                     params={"symbol": pair}, timeout=10).json()
    return {"symbol": symbol.upper(), "pair": pair, "price": float(d["lastPrice"]),
            "change": float(d["priceChange"]), "change_pct": float(d["priceChangePercent"]),

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 · references/api-endpoints.md (reported line 16)May include surrounding context.

md
if not coin_id:
        return {"error": f"Unknown coin: {symbol}. Supported: {', '.join(COIN_MAP.keys())}"}
    wait_if_needed("coingecko")
    data = requests.get("https://api.coingecko.com/api/v3/simple/price",
                        params={"ids": coin_id, "vs_currencies": "try,usd",
                                "include_24hr_change": "true"}, timeout=10).json().get(coin_id, {})
    return {"symbol": symbol.upper(), "price_try": data.get("try", 0),

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/crypto.py (reported line 47)May include surrounding context.

python
if not coin_id:
        return {"error": f"Unknown coin: {symbol}. Supported: {', '.join(COIN_MAP.keys())}"}
    wait_if_needed("coingecko")
    data = requests.get("https://api.coingecko.com/api/v3/simple/price",
                        params={"ids": coin_id, "vs_currencies": "try,usd",
                                "include_24hr_change": "true"}, timeout=10).json().get(coin_id, {})
    return {"symbol": symbol.upper(), "price_try": data.get("try", 0),

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 · references/api-endpoints.md (reported line 29)May include surrounding context.

md
api_key = get_api_key("fredApiKey")
    if not api_key: return {"error": "FRED API key not configured. Add FRED_API_KEY to finclaw env config."}
    wait_if_needed("fred")
    data = requests.get("https://api.stlouisfed.org/fred/series/observations",
        params={"series_id": series_id, "api_key": api_key, "file_type": "json",
                "sort_order": "desc", "limit": limit}, timeout=10).json()
    return {"series_id": series_id,

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/macro.py (reported line 24)May include surrounding context.

python
api_key = get_api_key("fredApiKey")
    if not api_key: return {"error": "FRED API key not configured. Add FRED_API_KEY to finclaw env config."}
    wait_if_needed("fred")
    data = requests.get("https://api.stlouisfed.org/fred/series/observations",
        params={"series_id": series_id, "api_key": api_key, "file_type": "json",
                "sort_order": "desc", "limit": limit}, timeout=10).json()
    return {"series_id": series_id,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The remove_position function permanently deletes a portfolio position and all associated transactions immediately after receiving a symbol, with no confirmation prompt, soft-delete mechanism, or safeguard against accidental invocation. In a finance assistant context, this can cause irreversible loss of user portfolio history and undermine tracking, reporting, and auditability even if caused by user error or an upstream agent mistake.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/setup.py (reported line 20)May include surrounding context.

python
print(f"Venv already exists at {VENV_DIR}")
        return
    print(f"Creating venv at {VENV_DIR}...")
    subprocess.check_call([sys.executable, "-m", "venv", VENV_DIR])
    print("Venv created.")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/setup.py (reported line 26)May include surrounding context.

python
def install_deps():
    print("Installing dependencies...")
    subprocess.check_call([VENV_PIP, "install", "--upgrade", "pip"],
                          stdout=subprocess.DEVNULL)
    subprocess.check_call([VENV_PIP, "install", "-r", REQ_FILE])
    print("Dependencies installed.")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
80% confidence
Finding

Installing dependencies from requirements.txt during setup introduces a meaningful supply-chain risk: any compromised, malicious, or unpinned dependency will execute code during installation with the user's privileges. In a skill context, this is more dangerous because setup is likely to be run automatically or with user trust, making dependency execution a practical attack path.

Content

Scanner excerpt · scripts/setup.py (reported line 28)May include surrounding context.

python
print("Installing dependencies...")
    subprocess.check_call([VENV_PIP, "install", "--upgrade", "pip"],
                          stdout=subprocess.DEVNULL)
    subprocess.check_call([VENV_PIP, "install", "-r", REQ_FILE])
    print("Dependencies installed.")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
78% confidence
Finding

This setup script automatically executes another local Python script as part of initialization, which expands the trusted code execution surface during installation. If the skill package or repository contents are tampered with, init_db.py will run immediately with the user's privileges, making installation-time compromise easier.

Content

Scanner excerpt · scripts/setup.py (reported line 34)May include surrounding context.

python
def init_db():
    print("Initializing database...")
    subprocess.check_call([VENV_PYTHON, os.path.join(SKILL_DIR, "scripts", "init_db.py")])


def main():

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code performs irreversible deletion of user data from both watchlist_items and watchlists. Although the function returns a status message after the fact, there is no confirmation prompt, pre-action warning, or explanatory comment/docstring disclosing the destructive behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill relies on multiple third-party services for quotes, crypto, forex, news, macro, and sentiment data, but it does not give a clear privacy notice that user-requested symbols, watchlists, or research targets may be sent to external providers. In a finance context, even ticker interest can reveal sensitive investment intent, making undisclosed outbound sharing a real privacy concern.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.