Back to skill

Security audit

Stock Deep Dive

Security checks for vulnerabilities and agentic risk

Overview

This stock-research skill mostly matches its stated purpose, but its optional X/Twitter integration handles live session tokens too broadly and depends on unpinned external tools.

Review before installing. Use a dedicated environment, do not place unrelated secrets in the skill .env, prefer scoped secret injection for only AUTH_TOKEN and CT0, pin/audit dependencies, and avoid global bird installation unless you trust and can verify that tool. Treat the generated financial analysis as informational only.

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
scripts/install_deps.py:7
Finding
Unpinned Third-Party Dependencies Permit Mutable Installation-Time Code## Vulnerability Details **File Location**: `scripts/install_deps.py:7-16`; additional dependency declarations appear in `stock-analysis/pyproject.toml:7-50` and the optional global npm installation instruction appears in `stock-analysis/SKILL.md:151` **Vulnerability Type**: Unpinned executable dependencies and insufficient supply-chain integrity controls **Risk Level**: Medium ### Complete Code Snippet ```python def main(): skill_dir = Path(__file__).parent venv_dir = skill_dir / '.venv' # uv venv subprocess.run([sys.executable, '-m', 'uv', 'venv', str(venv_dir)], check=True) # Activate & pip pip = venv_dir / 'bin' / 'pip' subprocess.run([pip, 'install', 'yfinance', 'pandas', 'numpy', 'requests', 'jsonschema'], check=True) ``` The project also declares numerous dependencies without version constraints: ```toml dependencies = [ "yfinance", "pandas", "numpy", "ta", "rich", "tabulate", "pytz", "requests", "lxml", "beautifulsoup4", "feedparser", "tenacity", "humanize", "tqdm", "edgartools", "fear-and-greed", "httpx", "python-dateutil", "jinja2", "markdown-it-py", "rank-bm25", "rapidfuzz", "textdistance", "unidecode", "curl-cffi", "frozendict", "filelock", "platformdirs", "pycparser", "soupsieve", "sgmllib3k", "nest-asyncio", "multitasking", "pyarrow", "orjson", "peewee", "pydantic", "typing-extensions", "idna", "charset-normalizer", "urllib3", "certifi", "cffi", "h11", "httpcore", "pydantic-core", "attrs", "cattrs", "stamina", "markupsafe", "mdurl", "pygments", "pyrate-limiter", ] ``` ### Technical Analysis The installer resolves package names to whatever versi ...[truncated 1881 chars]
Remediation
## Remediation Suggestions 1. Pin every direct dependency to a reviewed version and constrain transitive resolution through a committed lockfile. 2. Generate and verify package hashes, such as with a hash-locked requirements file and `--require-hashes`. 3. Use a trusted, explicitly configured package index and disable unintended extra indexes to reduce dependency-confusion exposure. 4. Audit and remove dependencies that are not directly required. 5. Run installation inside a dedicated, non-privileged virtual environment. 6. Pin the documented `bird` npm package to a reviewed exact version and avoid global installation. 7. Add automated dependency vulnerability, provenance, and lockfile-drift checks to the release process.

T09 · Insecure Skill Coding Practices

Warning
Location
stock-analysis/scripts/hot_scanner.py:22
Finding
External Twitter CLI Receives the Entire Parent Environment and All Project Environment Variables## Vulnerability Details **File Location**: `stock-analysis/scripts/hot_scanner.py:22-30,387-391`; the same pattern occurs in `stock-analysis/scripts/rumor_scanner.py:30-38,79-81,132-134` **Vulnerability Type**: Excessive disclosure of environment variables to a third-party executable **Risk Level**: Medium ### Complete Code Snippet In `stock-analysis/scripts/hot_scanner.py`: ```python # Load .env file if exists ENV_FILE = Path(__file__).parent.parent / ".env" if ENV_FILE.exists(): with open(ENV_FILE) as f: for line in f: line = line.strip() if line and not line.startswith("#") and "=" in line: key, value = line.split("=", 1) os.environ[key] = value ``` ```python env = os.environ.copy() result = subprocess.run( [bird_bin, "search", query, "-n", "15", "--json"], capture_output=True, text=True, timeout=30, env=env ) ``` In `stock-analysis/scripts/rumor_scanner.py`: ```python BIRD_CLI = "/home/clawdbot/.nvm/versions/node/v24.12.0/bin/bird" BIRD_ENV = Path(__file__).parent.parent / ".env" def load_env(): """Load environment variables from .env file.""" if BIRD_ENV.exists(): for line in BIRD_ENV.read_text().splitlines(): if '=' in line and not line.startswith('#'): key, value = line.split('=', 1) os.environ[key.strip()] = value.strip().strip('"').strip("'") ``` ```python cmd = [BIRD_CLI, 'search', query, '-n', '10', '--json'] env = os.environ.copy() result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, env=env) ``` ```python cmd = [BIRD_CLI, 'search', query, '-n', '15', '--json'] env = os.environ.copy() result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, env=env) ``` ### Technical Analysis The documented X integration requires only `AUTH_TOKEN` and `CT0`. Instead of supplying only thos ...[truncated 2338 chars]
Remediation
## Remediation Suggestions 1. Parse only the explicitly required `AUTH_TOKEN` and `CT0` keys from `.env`. 2. Do not copy `.env` values into global `os.environ`. 3. Construct a minimal child environment containing only required authentication values and essential runtime variables such as a controlled `PATH`, locale, and certificate configuration. 4. Resolve the `bird` executable to a configured absolute path and verify that it is the expected reviewed installation. 5. Reject unknown `.env` keys or keep unrelated credentials in separate, purpose-specific secret stores. 6. Require restrictive filesystem permissions on `.env`, such as user-only read and write access. 7. Prefer a supported API client with explicit credential parameters over passing secrets through a broad process environment. 8. Pin and verify the external CLI as described in the dependency remediation.
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (84)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
GET  /portfolios
   POST /portfolios
   PUT  /portfolios/{id}
   DELETE /portfolios/{id}

   GET  /portfolios/{id}/assets
   POST /portfolios/{id}/assets
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
GET  /portfolios/{id}/assets
   POST /portfolios/{id}/assets
   PUT  /portfolios/{id}/assets/{ticker}
   DELETE /portfolios/{id}/assets/{ticker}

   GET  /portfolios/{id}/performance?period=weekly
   GET  /portfolios/{id}/summary
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
GET  /alerts
   POST /alerts
   DELETE /alerts/{id}

   GET  /user/subscription
   POST /user/subscription/upgrade
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Self-Modification

High
Category
Rogue Agent
Content
- [ ] Add timeout per indicator (10s max)
- [ ] Test with multiple stocks in sequence
- [ ] Measure actual runtime improvement
- [ ] Update SKILL.md with new runtime (target: 3-4s)

**Expected Impact**:
- Reduce runtime from 6-10s to 3-4s per stock
Confidence
85% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- [ ] Add timeout per indicator (10s max)
- [ ] Test with multiple stocks in sequence
- [ ] Measure actual runtime improvement
- [ ] Update SKILL.md with new runtime (target: 3-4s)

**Expected Impact**:
- Reduce runtime from 6-10s to 3-4s per stock
Confidence
85% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Credential Access

High
Category
Privilege Escalation
Content
Create `.env` file in the skill directory:

```bash
# /path/to/stock-analysis/.env
AUTH_TOKEN=your_auth_token_here
CT0=your_ct0_token_here
```
Confidence
93% confidence
Finding
The example directs users to place live authentication tokens in a local .env file inside the project directory, which is a common source of accidental disclosure through version control, backups, debugging output, and local file access by other tools. In an automation-oriented skill, this is more dangerous because users may operationalize the setup and propagate secrets into scripts, caches, or shared environments.

Credential Access

High
Category
Privilege Escalation
Content
from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor, as_completed

# Load .env file if exists
ENV_FILE = Path(__file__).parent.parent / ".env"
if ENV_FILE.exists():
    with open(ENV_FILE) as f:
Confidence
91% confidence
Finding
Accessing a repository-local `.env` file is not inherently malicious, but in this skill it increases security risk because secrets are later made available to an external subprocess. The credential access is broader than necessary for simple trend scanning and lacks clear scoping or disclosure.

Credential Access

High
Category
Privilege Escalation
Content
from concurrent.futures import ThreadPoolExecutor, as_completed

# Load .env file if exists
ENV_FILE = Path(__file__).parent.parent / ".env"
if ENV_FILE.exists():
    with open(ENV_FILE) as f:
        for line in f:
Confidence
91% confidence
Finding
The `.env` existence check is part of a broader pattern of secret harvesting from local configuration. In context, this matters because the script later invokes a third-party CLI with inherited environment variables, creating a plausible exfiltration path.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
for category, query in searches:
                try:
                    env = os.environ.copy()
                    result = subprocess.run(
                        [bird_bin, "search", query, "-n", "15", "--json"],
                        capture_output=True, text=True, timeout=30, env=env
Confidence
99% confidence
Finding
`os.environ.copy()` captures the entire environment, including any secrets loaded from `.env` or inherited from the host, and passes them to an external CLI. This unnecessarily expands the trust boundary and can leak credentials, API keys, tokens, or internal configuration to another executable.

Credential Access

High
Category
Privilege Escalation
Content
# Bird CLI path
BIRD_CLI = "/home/clawdbot/.nvm/versions/node/v24.12.0/bin/bird"
BIRD_ENV = Path(__file__).parent.parent / ".env"

def load_env():
    """Load environment variables from .env file."""
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
BIRD_ENV = Path(__file__).parent.parent / ".env"

def load_env():
    """Load environment variables from .env file."""
    if BIRD_ENV.exists():
        for line in BIRD_ENV.read_text().splitlines():
            if '=' in line and not line.startswith('#'):
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
for query in queries[:4]:  # Limit to avoid rate limits
        try:
            cmd = [BIRD_CLI, 'search', query, '-n', '10', '--json']
            env = os.environ.copy()
            
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, env=env)
Confidence
97% confidence
Finding
Copying the full parent environment and passing it to an external CLI can leak unrelated secrets such as API keys, tokens, and cloud credentials to that child process. In a skill context, this is more dangerous because agent runtimes often inject many sensitive variables, and the external tool may perform network operations that could exfiltrate them indirectly.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
for query in queries[:3]:
        try:
            cmd = [BIRD_CLI, 'search', query, '-n', '15', '--json']
            env = os.environ.copy()
            
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, env=env)
Confidence
97% confidence
Finding
This second call repeats the same risky pattern of forwarding the entire environment to a network-capable third-party CLI. If the process environment contains credentials unrelated to Twitter/news access, those secrets become accessible to the child process without necessity.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
import subprocess, json, sys
ticker = sys.argv[1]
raw = {'mock': '8-dim from stock-analysis'}  # Deprecated, use collect_deep.py
panel = json.loads(subprocess.check_output(['python3', 'investor_panel.py', ticker]))
dcf = subprocess.check_output(['python3', 'fin_models.py', ticker]).decode()
raw['uzi_panel'] = panel
raw['uzi_dcf'] = dcf
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
ticker = sys.argv[1]
raw = {'mock': '8-dim from stock-analysis'}  # Deprecated, use collect_deep.py
panel = json.loads(subprocess.check_output(['python3', 'investor_panel.py', ticker]))
dcf = subprocess.check_output(['python3', 'fin_models.py', ticker]).decode()
raw['uzi_panel'] = panel
raw['uzi_dcf'] = dcf
print(json.dumps(raw))
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# analyze_stock --json
try:
    result = subprocess.run(['uv', 'run', '/data/workspace/skills/stock-analysis/scripts/analyze_stock.py', ticker, '--output', 'json'], capture_output=True, text=True, timeout=30)
    raw['analyze_stock'] = json.loads(result.stdout) if result.returncode == 0 else {'error': result.stderr}
except Exception as e:
    raw['analyze_stock'] = {'error': str(e)}
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# fundamentals
try:
    result = subprocess.run(['uv', 'run', '/data/workspace/skills/stock-fundamentals/src/main.py', ticker], capture_output=True, text=True, timeout=30)
    raw['fundamentals'] = result.stdout.strip()
except Exception as e:
    raw['fundamentals'] = {'error': str(e)}
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# dividend
try:
    result = subprocess.run(['uv', 'run', '/data/workspace/skills/stock-analysis/scripts/dividends.py', ticker], capture_output=True, text=True, timeout=30)
    raw['dividend'] = result.stdout.strip() if 'does not pay' not in result.stdout else None
except Exception as e:
    raw['dividend'] = None
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# momentum (reuse analyze --fast for RSI/vol/mom extract)
try:
    result = subprocess.run(['uv', 'run', '/data/workspace/skills/stock-analysis/scripts/analyze_stock.py', ticker, '--fast', '--output', 'json'], capture_output=True, text=True, timeout=30)
    mom = json.loads(result.stdout)['components']['momentum'] if result.returncode == 0 else {}
    raw['momentum'] = mom
except Exception as e:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Deep Dive Collector: Parallel 8-dim + UZI
import subprocess, json, sys
ticker = sys.argv[1]
raw_analyze = subprocess.check_output(['/data/workspace/skills/stock-analysis/scripts/analyze_stock.py', ticker]).decode()
raw_div = json.dumps({'ticker': ticker, 'dividend': 'no_dividend'})
raw_fund = subprocess.check_output(['/data/workspace/skills/stock-fundamentals/src/main.py', ticker]).decode()
# Parse RSI/vol from analyze text
Confidence
72% confidence
Finding
This code executes another script using attacker-influenced input (`ticker`) and trusts the returned output without validation. Although it avoids shell metacharacter injection by using argument lists, it still expands the attack surface to another executable component whose argument parsing or runtime behavior may be unsafe.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
No manifest is available, so there is no stated purpose that justifies this file's broad orchestration behavior. The code invokes several other local programs, aggregates their outputs, and runs a final gate step, which is a materially broader capability than a narrowly scoped utility script and cannot be validated against any declared intent.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This Python file invokes multiple external scripts via subprocess.check_output, including local analyzers and model/gating scripts. Although comments describe some behavior, there is no confirmation prompt, user-facing print/log before execution, or docstring warning that the skill will execute other programs with user-supplied input.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
ticker = sys.argv[1]
raw_analyze = subprocess.check_output(['/data/workspace/skills/stock-analysis/scripts/analyze_stock.py', ticker]).decode()
raw_div = json.dumps({'ticker': ticker, 'dividend': 'no_dividend'})
raw_fund = subprocess.check_output(['/data/workspace/skills/stock-fundamentals/src/main.py', ticker]).decode()
# Parse RSI/vol from analyze text
import re
rsi_match = re.search(r'RSI.*?(\d+\.\d+)', raw_analyze)
Confidence
72% confidence
Finding
The script launches a second external analysis program with the same unvalidated user-controlled ticker and fully trusts its output. This is risky because any weakness in the downstream script becomes reachable through this wrapper, and failures or malicious output can poison the final JSON payload.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
vol = vol_match.group(1) if vol_match else 'unknown'
enrich = {'analyze': raw_analyze, 'div': raw_div, 'fund': raw_fund, 'rsi': rsi, 'vol': vol}
panel = {'avg': 5.0, 'votes': ['+10 ROE Buffett', '+0 growth Lynch']}  # Self-contained mock until parse full
dcf = subprocess.check_output(['python3', 'fin_models.py', ticker]).decode()
enrich = {'analyze': raw_analyze, 'div': raw_div, 'fund': raw_fund, 'panel': panel, 'dcf': dcf}
raw = json.dumps(enrich)
print(json.dumps(enrich))
Confidence
74% confidence
Finding
Executing `python3 fin_models.py` by relative filename depends on the current working directory and local file contents, making it easier to run an unintended script if the environment is manipulated. The unvalidated ticker is also forwarded into that component, further extending risk into downstream code.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
enrich = {'analyze': raw_analyze, 'div': raw_div, 'fund': raw_fund, 'panel': panel, 'dcf': dcf}
raw = json.dumps(enrich)
print(json.dumps(enrich))
gate = json.loads(subprocess.check_output(['python3', 'uzi_gate.py'], input=json.dumps(enrich)))
print(json.dumps({'enrich': enrich, 'gate': gate}))
Confidence
76% confidence
Finding
The script invokes a local Python program and passes a large serialized object to it without validating the downstream program's trustworthiness or constraining its behavior. While there is no shell injection here because arguments are passed as a list, this still creates an execution boundary where a compromised or swapped `uzi_gate.py` could process sensitive data or perform unintended actions.

Static analysis

No suspicious patterns detected.