Back to skill

Security audit

stock-screener

Security checks for vulnerabilities and agentic risk

Overview

This stock research skill is mostly transparent, but optional third-party research backends receive broader local API/model credentials than they need.

Review before installing if your OpenClaw host has valuable API keys in environment variables. Prefer running this skill in a separate account or sandbox with only the credentials needed for the specific provider, avoid optional bootstraps unless you trust the third-party packages and datasets, and treat AI4Trade confirm=true actions as real platform/account changes even though they do not place broker orders.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
server.py:130
Finding

Excessive Exposure of Host LLM Credentials to Third-Party Research Backends

Content
View full analysis
Dict[str, str]: """Pass only explicitly supported LLM settings to optional local backends.""" env = { "HOME": os.path.expanduser("~"), "PATH": os.environ.get("PATH", ""), "LANG": os.environ.get("LANG", "en_US.UTF-8"), "LC_ALL": os.environ.get("LC_ALL", ""), "PYTHONIOENCODING": "utf-8", "TRADINGAGENTS_MEMORY_LOG_PATH": os.path.join(_ensure_quant_state_dir(), "tradingagents_memory.md"), "TRADINGAGENTS_CACHE_DIR": os.path.join(_ensure_quant_state_dir(), "tradingagents_cache"), } for name in _EXTERNAL_LLM_ENV_NAMES: if os.getenv(name): env[name] = os.environ[name] return {key: value for key, value in env.items() if value != ""} ``` The resulting environment is used for deterministic Alpha Zoo operations: ```python result = _run_research_backend(args, timeout_seconds) ``` It is also inherited by QuantaAlpha: ```python def _quantaalpha_env() -> Dict[str, str]: paths = _quantaalpha_paths() environment = _backend_env() environment.update( { "QLIB_DATA_DIR": paths["qlib"], "QLIB_PROVIDER_URI": paths["qlib"], "DATA_RESULTS_DIR": paths["results"], ...[truncated 3159 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
server.py:295
Finding

Credential-Bearing HTTP Requests Do Not Enforce Redirect Destination Policies

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/bootstrap_vibe_trading.py:30
Finding

Vibe-Trading Bootstrap Installs and Executes Dependencies Without Hash Verification

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/bootstrap_quantaalpha.py:32
Finding

QuantaAlpha Bootstrap Does Not Verify Dependency or Dataset Artifacts

Content
View full analysis
None: destination.parent.mkdir(parents=True, exist_ok=True) if destination.is_file() and destination.stat().st_size > 0: print(f"Using existing {destination}") return partial = destination.with_suffix(destination.suffix + ".part") request = urllib.request.Request(url, headers={"User-Agent": "stock-screener-pro/3.8.1"}) with urllib.request.urlopen(request, timeout=60) as response, partial.open("wb") as output: total = int(response.headers.get("Content-Length", "0")) copied = 0 while True: chunk = response.read(1024 * 1024) if not chunk: break output.write(chunk) copied += len(chunk) if total and copied % (64 * 1024 * 1024) < len(chunk): print(f" downloaded {copied / 1024 / 1024:.0f}/{total / 1024 / 1024:.0f} MiB", flush=True) partial.replace(destination) ``` The repository commit is pinned, but its dependencies are installed without a hash-locked dependency set: ```python def install_repository(repo_dir: Path, venv_dir: Path) -> Path: if not repo_dir.exists(): repo_dir.parent.mkdir(parents=True, exist_ok=True) run(["git", "clone", "--filter=blob:none", REPOSITORY, str(repo_dir)]) if not (repo_dir / ".git").is_dir(): raise RuntimeError(f"Existing path is not a Git checkout: {repo_dir}") status = subprocess.run( ["git", "status", "--porcelain"], cwd=repo_dir, capture_output=True, text=True, check=True ).stdout.strip() if status: raise RuntimeError(f"QuantaAlpha checkout has ...[truncated 3490 chars]
Remediation
View 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
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (46)

Tainted flow: 'request' from os.getenv (line 1454, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · server.py (reported line 302)May include surrounding context.

python
headers={"Content-Type": "application/json", "refresh_token": THS_REFRESH_TOKEN},
        method="POST",
    )
    with urllib.request.urlopen(request, timeout=10) as response:
        body = json.loads(response.read().decode("utf-8"))
    data = body.get("data", {}) if isinstance(body, dict) else {}
    token = data.get("access_token") if isinstance(data, dict) else None

Tainted flow: 'request' from os.getenv (line 1454, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · server.py (reported line 332)May include surrounding context.

python
method="POST",
        )
        try:
            with urllib.request.urlopen(request, timeout=20) as response:
                body = json.loads(response.read().decode("utf-8"))
            error_code = body.get("errorcode") if isinstance(body, dict) else None
            if error_code not in (None, 0, "0"):

Tainted flow: 'request' from os.getenv (line 1454, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · server.py (reported line 1455)May include surrounding context.

python
method="POST",
        )
        try:
            with urllib.request.urlopen(request, timeout=20) as response:
                body = json.loads(response.read().decode("utf-8"))
            error_code = body.get("errorcode") if isinstance(body, dict) else None
            if error_code not in (None, 0, "0"):

Tainted flow: 'request' from os.getenv (line 1454, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · server.py (reported line 2853)May include surrounding context.

python
method="POST",
        )
        try:
            with urllib.request.urlopen(request, timeout=20) as response:
                body = json.loads(response.read().decode("utf-8"))
            error_code = body.get("errorcode") if isinstance(body, dict) else None
            if error_code not in (None, 0, "0"):

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · server.py (reported line 412)May include surrounding context.

python
url = f"https://qt.gtimg.cn/q={','.join(codes_query)}"
        try:
            req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
            with urllib.request.urlopen(req, timeout=timeout) as res:
                text = res.read().decode("gbk", errors="ignore")
            chunk_res = []
            for line in text.strip().split(";\n"):

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · server.py (reported line 2630)May include surrounding context.

python
url = f"https://qt.gtimg.cn/q={','.join(codes_query)}"
        try:
            req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
            with urllib.request.urlopen(req, timeout=timeout) as res:
                text = res.read().decode("gbk", errors="ignore")
            chunk_res = []
            for line in text.strip().split(";\n"):

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · server.py (reported line 508)May include surrounding context.

python
url = f"https://ifzq.gtimg.cn/appstock/app/fqkline/get?param={sym},day,,,{count},qfq"
    try:
        req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
        with urllib.request.urlopen(req, timeout=5) as res:
            data = json.loads(res.read().decode("utf-8"))
        raw = data.get("data", {}).get(sym, {}).get("qfqday", [])
        if not raw:

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · server.py (reported line 622)May include surrounding context.

python
f"?param={symbol_code},day,{start},{end},{count},qfq"
        )
        req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0", "Referer": "https://gu.qq.com/"})
        with urllib.request.urlopen(req, timeout=30) as resp:
            d = json.loads(resp.read().decode("utf-8"))
        node = d.get("data", {}).get(symbol_code, {})
        return node.get("qfqday") or node.get("day", [])

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · server.py (reported line 1400)May include surrounding context.

python
url = f"{safe_base_url}/api/v1/auth/login"
        payload = json.dumps({"password": DSA_PASSWORD}).encode("utf-8")
        req = urllib.request.Request(url, data=payload, headers={"Content-Type": "application/json"})
        with urllib.request.urlopen(req, timeout=4) as res:
            cookie_header = res.headers.get("Set-Cookie", "")
            if "dsa_session=" in cookie_header:
                for part in cookie_header.split(";"):

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared purpose presents the skill as a stock research and screening tool, but the content also includes bootstrap scripts, virtual environment creation, git checkout of external code, and large dataset downloads with filesystem modification. That mismatch is dangerous because users may invoke or install the skill expecting passive analysis, while it can perform supply-chain-sensitive setup actions that execute external code and alter the host environment.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
,例如 `openclaw models set deepseek/deepseek-v4-pro`;Skill 会在下一次研究时自动采用它。不要在工具参数、`SKILL.md` 或聊天中填写模型 Key。

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/bootstrap_quantaalpha.py (reported line 96)May include surrounding context.

python
venv_python = venv_dir / "bin" / "python"
    if not venv_python.exists():
        run([sys.executable, "-m", "venv", str(venv_dir)])
    install_env = dict(os.environ)
    install_env["SETUPTOOLS_SCM_PRETEND_VERSION"] = "0.1.0"
    run([str(venv_python), "-m", "pip", "install", "--upgrade", "pip"], env=install_env)
    run([str(venv_python), "-m", "pip", "install", "-e", str(repo_dir)], env=install_env)

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 · server.py (reported line 64)May include surrounding context.

python
DSA_PASSWORD = os.getenv("DSA_ADMIN_PASSWORD", "")

# Official Tonghuashun/iFinD HTTP QuantAPI.  The endpoint is intentionally not
# configurable so a refresh/access token cannot be redirected to another host.
THS_API_BASE_URL = "https://quantapi.51ifind.com"
THS_REFRESH_TOKEN = os.getenv("THS_REFRESH_TOKEN", os.getenv("IFIND_REFRESH_TOKEN", "")).strip()
THS_ACCESS_TOKEN = os.getenv("THS_ACCESS_TOKEN", "").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 · server.py (reported line 286)May include surrounding context.

python
DSA_PASSWORD = os.getenv("DSA_ADMIN_PASSWORD", "")

# Official Tonghuashun/iFinD HTTP QuantAPI.  The endpoint is intentionally not
# configurable so a refresh/access token cannot be redirected to another host.
THS_API_BASE_URL = "https://quantapi.51ifind.com"
THS_REFRESH_TOKEN = os.getenv("THS_REFRESH_TOKEN", os.getenv("IFIND_REFRESH_TOKEN", "")).strip()
THS_ACCESS_TOKEN = os.getenv("THS_ACCESS_TOKEN", "").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 · server.py (reported line 307)May include surrounding context.

python
DSA_PASSWORD = os.getenv("DSA_ADMIN_PASSWORD", "")

# Official Tonghuashun/iFinD HTTP QuantAPI.  The endpoint is intentionally not
# configurable so a refresh/access token cannot be redirected to another host.
THS_API_BASE_URL = "https://quantapi.51ifind.com"
THS_REFRESH_TOKEN = os.getenv("THS_REFRESH_TOKEN", os.getenv("IFIND_REFRESH_TOKEN", "")).strip()
THS_ACCESS_TOKEN = os.getenv("THS_ACCESS_TOKEN", "").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 · server.py (reported line 2722)May include surrounding context.

python
DSA_PASSWORD = os.getenv("DSA_ADMIN_PASSWORD", "")

# Official Tonghuashun/iFinD HTTP QuantAPI.  The endpoint is intentionally not
# configurable so a refresh/access token cannot be redirected to another host.
THS_API_BASE_URL = "https://quantapi.51ifind.com"
THS_REFRESH_TOKEN = os.getenv("THS_REFRESH_TOKEN", os.getenv("IFIND_REFRESH_TOKEN", "")).strip()
THS_ACCESS_TOKEN = os.getenv("THS_ACCESS_TOKEN", "").strip()

Direct flow: os.environ.get (credential/environment) → subprocess.run (code execution)

High
Category
Data Flow
Confidence
80% confidence
Finding

The subprocess uses a CLI path resolved from the environment PATH, making code execution depend on host-controlled process environment. In an agent/server deployment, a poisoned PATH or substituted binary can convert normal tool use into arbitrary code execution with access to the server's data and configured credentials.

Content

Scanner excerpt · server.py (reported line 1669)May include surrounding context.

python
self._respond(400, {"error": {"message": f"invalid request: {exc}"}})
                    return
                try:
                    completed = subprocess.run(
                        [bridge.cli, "infer", "model", "run", "--gateway", "--json", "--prompt", prompt],
                        capture_output=True,
                        text=True,

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code includes state-changing AI4Trade actions such as follow/unfollow, publishing content, accepting replies, exchanging points, and publishing realtime signals, which exceed a stock screener's expected read-only research role. Even with confirm flags, these actions can manipulate external accounts and influence followers, creating real operational and reputational risk if an agent invokes them unexpectedly.

Content

No source excerpt is available for this finding.

Self-Modification

High
Category
Rogue Agent
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.

Content

Scanner excerpt · tests/test_server.py (reported line 7)May include surrounding context.

python
import json
import numpy as np
import pandas as pd
from unittest.mock import patch


SKILL_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill advertises broad capabilities that imply access to environment variables, filesystem reads/writes, network, and shell execution, but it does not declare any explicit tool scope or permission boundaries. This makes the trust boundary ambiguous: a caller or host may approve a seemingly research-oriented skill without realizing it can invoke installation scripts, access secrets from the environment, and modify the local system.

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

python
def run(command: list[str], *, cwd: Path | None = None, env: dict[str, str] | None = None) -> None:
    printable = " ".join(command)
    print(f"+ {printable}", flush=True)
    subprocess.run(command, cwd=cwd, env=env, check=True)


def download(url: str, destination: Path) -> None:

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

python
run(["git", "clone", "--filter=blob:none", REPOSITORY, str(repo_dir)])
    if not (repo_dir / ".git").is_dir():
        raise RuntimeError(f"Existing path is not a Git checkout: {repo_dir}")
    status = subprocess.run(
        ["git", "status", "--porcelain"], cwd=repo_dir, capture_output=True, text=True, check=True
    ).stdout.strip()
    if status:

Tainted flow: 'repo_dir' from os.environ.get (line 127, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/bootstrap_quantaalpha.py (reported line 85)May include surrounding context.

python
run(["git", "clone", "--filter=blob:none", REPOSITORY, str(repo_dir)])
    if not (repo_dir / ".git").is_dir():
        raise RuntimeError(f"Existing path is not a Git checkout: {repo_dir}")
    status = subprocess.run(
        ["git", "status", "--porcelain"], cwd=repo_dir, capture_output=True, text=True, check=True
    ).stdout.strip()
    if status:

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

python
download(f"{DATASET_BASE}/daily_pv.h5?download=true", full_path)
        print(f"Full factor source ready at {full_path}")

    installed_commit = subprocess.run(
        ["git", "rev-parse", "HEAD"], cwd=repo_dir, capture_output=True, text=True, check=True
    ).stdout.strip()
    print(f"QuantaAlpha ready: python={venv_python}")

Tainted flow: 'repo_dir' from os.environ.get (line 127, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/bootstrap_quantaalpha.py (reported line 144)May include surrounding context.

python
download(f"{DATASET_BASE}/daily_pv.h5?download=true", full_path)
        print(f"Full factor source ready at {full_path}")

    installed_commit = subprocess.run(
        ["git", "rev-parse", "HEAD"], cwd=repo_dir, capture_output=True, text=True, check=True
    ).stdout.strip()
    print(f"QuantaAlpha ready: python={venv_python}")

Static analysis

No suspicious patterns detected.