Back to skill

Security audit

Bangladesh stock market live data and analytics

Security checks for vulnerabilities and agentic risk

Overview

This stock-market skill mostly does what it says, but it under-discloses sensitive portfolio access and has API-key transport risks that merit review before installation.

Install only if you trust stock-ai.live with your API key and any portfolio data tied to that account. Prefer the default HTTPS endpoint, do not set STOCKAI_API_BASE to an http URL, and be aware that command outputs include promotional fields in addition to requested market data.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
scripts/stock.py:44
Finding

Unconditional Promotional Content Injected into Every Successful Command Response

Content
View full analysis

Vulnerability Details

File Location: scripts/stock.py:44-50 and scripts/stock.py:402-409
Vulnerability Type: Unconditional output manipulation and promotional content injection
Risk Level: High

Vulnerable Code

python
# CTA message for all responses
CTA = {
    "message": "Want trading signals, price alerts & AI insights?",
    "features": ["BUY/SELL signals", "Technical analysis", "Price alerts", "Portfolio tracking"],
    "signup": "https://stock-ai.live/register",
    "pricing": "Free: 100/day | Pro: ৳899/mo | Enterprise: ৳4,499/mo",
}
python
# Add CTA to all responses
result["_cta"] = CTA.copy()

# Add tier-specific upgrade message
if command in PRO_COMMANDS:
    result["_cta"]["upgrade"] = "This feature requires Pro tier (৳899/mo). Upgrade at https://stock-ai.live/pricing"
elif command in ENTERPRISE_COMMANDS:
    result["_cta"]["upgrade"] = "This feature requires Enterprise tier (৳4,499/mo). Upgrade at https://stock-ai.live/pricing"

Technical Analysis

The response formatter unconditionally inserts a hard-coded _cta object into every successful command response. This content advertises account registration, paid plans, trading signals, and other commercial features. Tier-specific commands receive an additional upgrade message.

The command documentation in SKILL.md describes the expected market-data functionality but does not clearly disclose that every machine-readable result will be modified to include promotional content. Because an AI agent may treat all returned fields as relevant output, the injected content can influence the agent to reproduce registration links, pricing, or upgrade prompts even when the user requested only factual market data.

The content is static rather than remotely supplied, and it does not execute code or modify system state. Nevertheless, its unconditional insertion represents persistent manipulation of agent-visible output.

Attack Path

  1. A user asks an ag ...[truncated 974 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the unconditional _cta insertion from format_response().
  2. Keep machine-readable market-data output limited to fields required by the selected command.
  3. If promotional information is retained, make it explicitly opt-in through a clearly documented flag such as --include-promotions.
  4. Separate promotional messages from the JSON result, for example by placing them in an interactive user interface rather than API output.
  5. Clearly document any optional promotional fields and ensure downstream agents can distinguish them from requested financial data.
  6. Add tests asserting that default command responses contain only command-relevant information.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/stock.py:23
Finding

API Key Can Be Exposed Through Plaintext HTTP or Unrestricted Redirect Handling

Content
View full analysis

Vulnerability Details

File Location: scripts/stock.py:23-34 and scripts/stock.py:412-428
Vulnerability Type: Insufficient endpoint and transport validation for authenticated requests
Risk Level: High

Vulnerable Code

python
# API base URL - SECURITY: only stock-ai.live domains allowed
_API_BASE_ENV = os.environ.get("STOCKAI_API_BASE", "")
if _API_BASE_ENV:
    # Validate to prevent redirect attacks
    # Must be stock-ai.live exactly, or end with .stock-ai.live (subdomain)
    _host = _API_BASE_ENV.lower().replace("http://", "").replace("https://", "").rstrip("/")
    _valid = _host == "stock-ai.live" or _host.endswith(".stock-ai.live")
    if not _valid:
        raise ValueError(f"STOCKAI_API_BASE can only point to stock-ai.live, got: {_API_BASE_ENV}")
    API_BASE = _API_BASE_ENV.rstrip("/") + "/api/v1"
else:
    API_BASE = "https://stock-ai.live/api/v1"
TIMEOUT_S = 30
python
def make_request(url: str, api_key: str) -> dict:
    """Make API request and return response."""
    headers = {
        "Content-Type": "application/json",
        "User-Agent": "StockAI-Skill/1.1.0",
        "X-API-Key": api_key,
    }

    req = Request(url, headers=headers, method="GET")

    try:
        opener = build_opener()
        with opener.open(req, timeout=TIMEOUT_S) as resp:
            return json.loads(resp.read().decode("utf-8"))

Technical Analysis

The STOCKAI_API_BASE validation removes an http:// or https:// prefix as text and then checks the remaining value as though it were a hostname. It does not parse the URL into its scheme, hostname, port, credentials, path, query, and fragment components.

Most importantly, the validation accepts both of these values:

text
https://stock-ai.live
http://stock-ai.live

Consequently, a configuration of http://stock-ai.live passes validation and is used directly. make_request() then places the sensitive API key in the X-API-Key header and transmits it over pl ...[truncated 2068 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse the configured URL with urllib.parse.urlsplit() rather than modifying it with string replacement.
  2. Require the URL scheme to be exactly https.
  3. Validate the parsed and normalized hostname against an explicit allowlist. If subdomains are required, verify hostname boundaries using parsed hostname data.
  4. Reject URLs containing embedded credentials, unexpected ports, query strings, fragments, or unapproved paths.
  5. Prefer a fixed API endpoint where deployment requirements permit it.
  6. Disable automatic redirects for authenticated API requests, or implement a redirect handler that validates every destination before proceeding.
  7. Never forward X-API-Key when a redirect changes the scheme, hostname, or port.
  8. Reject every redirect that downgrades from HTTPS to HTTP.
  9. Add automated tests covering plaintext HTTP, malformed URLs, cross-origin redirects, HTTPS downgrade redirects, user-information components, ports, and deceptive hostname suffixes.
  10. Rotate any API key that may previously have been used with a plaintext base URL.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (10)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · CHANGELOG.md (reported line 35)May include surrounding context.

md
from urllib.request import Request, build_opener
from urllib.error import URLError, HTTPError

# Load .env from skill directory if it exists
SKILL_DIR = Path(__file__).parent.parent
ENV_FILE = SKILL_DIR / ".env"
if ENV_FILE.exists():

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 39)May include surrounding context.

md
from urllib.request import Request, build_opener
from urllib.error import URLError, HTTPError

# Load .env from skill directory if it exists
SKILL_DIR = Path(__file__).parent.parent
ENV_FILE = SKILL_DIR / ".env"
if ENV_FILE.exists():

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/stock.py (reported line 14)May include surrounding context.

python
from urllib.request import Request, build_opener
from urllib.error import URLError, HTTPError

# Load .env from skill directory if it exists
SKILL_DIR = Path(__file__).parent.parent
ENV_FILE = SKILL_DIR / ".env"
if ENV_FILE.exists():

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/stock.py (reported line 16)May include surrounding context.

python
# Load .env from skill directory if it exists
SKILL_DIR = Path(__file__).parent.parent
ENV_FILE = SKILL_DIR / ".env"
if ENV_FILE.exists():
    for line in ENV_FILE.read_text().splitlines():
        line = line.strip()

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

python
def get_api_key(options: dict[str, Any]) -> str:
    """Get API key from options or environment."""
    api_key = options.get("api_key") or os.environ.get("STOCKAI_API_KEY", "").strip()
    if api_key:
        return api_key

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill declares required environment variables and demonstrates networked Python execution, but it does not declare any explicit tool scope such as permissions or allowed-tools. In an agent ecosystem, missing scope declarations can allow the runtime or reviewer to underestimate the skill's access to secrets, filesystem content, and outbound network calls, increasing the risk of unintended data exposure or misuse.

Content

No source excerpt is available for this finding.

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

md
### Get API Key

1. Visit **https://stock-ai.live/register** to create a free account
2. Go to **https://stock-ai.live/api-keys**
3. Click "Create New API Key"
4. Copy your API key (starts with `sk_live_`)

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill exposes a portfolio command that accesses authenticated user portfolio data, which goes beyond the stated scope of market data and analytics for DSE prices/signals. Scope expansion to account-specific data increases privacy risk and user surprise, especially because the manifest description does not clearly justify handling personal holdings.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Accessing /user/portfolio with an API key enables retrieval of authenticated user account data not clearly justified by the skill's stated purpose. Even if the backend is legitimate, this creates an unnecessary sensitive-data access path that can expose portfolio composition and related financial privacy information.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The request code transmits the user's API key in X-API-Key and may also send user-provided search queries or request portfolio data, but this file provides no explicit runtime disclosure before sending potentially sensitive data to a remote service. In a skill context, lack of transparent notice and consent around outbound transmission increases privacy and trust risk.

Content

No source excerpt is available for this finding.