Back to skill

Security audit

Qc Backtest Master

Security checks for vulnerabilities and agentic risk

Overview

This QuantConnect backtest skill is mostly purpose-aligned, but it can upload extra local files and silently use credential files beyond what the skill description clearly discloses.

Install only if you are comfortable giving it QuantConnect project mutation authority. Before running it, place strategy files in a clean directory, remove unrelated .json/.txt/.csv files, set QC_USER_ID, QC_API_TOKEN, and QC_PROJECT_ID explicitly, and check whether a parent .env or ~/.lean/credentials could be used unexpectedly. Treat early-stop as destructive because it deletes the backtest results.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
submit_backtest.py:31
Finding

Automatic Upload of Unselected Sibling Data Files

Content
View full analysis

Vulnerability Details

File Location: submit_backtest.py:31-41
Vulnerability Type: Excessive local file collection and unintended network disclosure
Risk Level: Medium

Vulnerable Code

python
# 2. Optionally add other relevant files in the same directory (excluding .py files to avoid conflicts)
valid_data_extensions = {'.json', '.txt', '.csv'}
for file in os.listdir(base_dir):
    ext = os.path.splitext(file)[1]
    if ext in valid_data_extensions:
        full_path = os.path.join(base_dir, file)
        try:
            with open(full_path, 'r', encoding='utf-8') as f:
                content = f.read()
            files_list.append({'name': file, 'content': content})
            print(f"Added data file: {file}")
        except Exception as e:
            print(f"Skipping data file {file}: {e}")

Technical Analysis

The submission process enumerates the strategy file's entire parent directory and automatically reads every file whose extension is .json, .txt, or .csv. These files are subsequently passed to QCApiClient.update_project_files() and uploaded to the configured QuantConnect cloud project.

The implementation does not require users to select auxiliary files explicitly and provides no confirmation step, filename allowlist, content sensitivity check, file-size limit, or upload manifest approval. File extensions alone do not establish that a file is necessary for the backtest. Such files may contain API credentials, proprietary datasets, customer information, trading records, or unrelated configuration data.

Although uploading explicitly selected strategy resources is part of the declared functionality, automatically collecting every matching sibling file exceeds the minimum local file access required.

Attack Path

  1. A user stores or selects a strategy in a directory containing unrelated .json, .txt, or .csv files.
  2. The user invokes `submit_backtest.p ...[truncated 1199 chars]
Remediation
View remediation

Remediation Suggestions

  • Upload only the file supplied through --main-file by default.
  • Add a repeatable explicit option such as --data-file path/to/file.csv for each required auxiliary file.
  • Resolve each supplied path with os.path.realpath() and verify that it is a regular file in an approved directory.
  • Display a complete upload manifest, including filenames and sizes, before initiating network transmission.
  • Require affirmative confirmation for auxiliary uploads in interactive use and provide an explicit non-interactive approval flag for automation.
  • Enforce per-file and aggregate upload-size limits.
  • Reject symbolic links unless their resolved targets have been explicitly approved.
  • Document precisely which local files will be read and uploaded.

T09 · Insecure Skill Coding Practices

Warning
Location
qc_api/config.py:5
Finding

Overbroad and Undocumented Credential Discovery

Content
View full analysis

Vulnerability Details

File Location: qc_api/config.py:5-52
Vulnerability Type: Excessive secret-file access and environment contamination
Risk Level: Medium

Vulnerable Code

python
def _load_dotenv():
    """Load environment variables from a .env file in the project root (does not overwrite existing vars)."""
    # Search upward from the current file's directory for a .env file
    search_dir = os.path.dirname(os.path.abspath(__file__))
    for _ in range(6):  # Search up to 6 levels
        env_path = os.path.join(search_dir, ".env")
        if os.path.exists(env_path):
            with open(env_path) as f:
                for line in f:
                    line = line.strip()
                    if not line or line.startswith('#') or '=' not in line:
                        continue
                    key, _, value = line.partition('=')
                    key = key.strip()
                    value = value.strip().strip('"').strip("'")
                    if key and key not in os.environ:
                        os.environ[key] = value
            return
        parent = os.path.dirname(search_dir)
        if parent == search_dir:
            break
        search_dir = parent


def get_credentials():
    """
    Read QuantConnect API credentials.
    Priority: environment variables > project .env file > ~/.lean/credentials file
    """
    # 0. Attempt to load from .env file
    _load_dotenv()

    # 1. Environment variables (including those loaded from .env)
    user_id = os.environ.get("QC_USER_ID")
    api_token = os.environ.get("QC_API_TOKEN")
    if user_id and api_token:
        return user_id, api_token

    # 2. Fallback to ~/.lean/credentials file
    lean_cred_path = os.path.expanduser("~/.lean/credentials")
    if os.path.exists(lean_cred_path):
        try:
            with open(lean_cred_path) as f:
                content = f.read(
...[truncated 3437 chars]
Remediation
View remediation

Remediation Suggestions

  • Use only the explicitly declared QC_USER_ID, QC_API_TOKEN, and QC_PROJECT_ID environment variables by default.
  • Do not search arbitrary parent directories for .env files.
  • If .env support is required, bind it to one documented, deterministic project-root path.
  • Parse only the three required QuantConnect keys instead of copying every entry into os.environ.
  • Return parsed configuration values directly rather than mutating global process environment state.
  • Make access to ~/.lean/credentials opt-in through an explicit flag such as --use-lean-credentials.
  • Warn the user and identify the selected credential source without printing secret values.
  • Validate .env and credential-file ownership and permissions before reading them.
  • Update SKILL.md to disclose every supported credential source and its precedence.
  • Avoid suppressing all credential-file exceptions; report safe, actionable errors without exposing secret contents.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill description emphasizes backtest orchestration, but the rules disclose an additional destructive action: auto-deleting an existing cloud file (Main.py) and overwriting main.py. Undisclosed destructive side effects are dangerous because users may invoke the skill expecting routine automation while it modifies or deletes project assets beyond what the high-level description suggests.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill description emphasizes backtest orchestration, but the rules disclose an additional destructive action: auto-deleting an existing cloud file (Main.py) and overwriting main.py. Undisclosed destructive side effects are dangerous because users may invoke the skill expecting routine automation while it modifies or deletes project assets beyond what the high-level description suggests.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill description emphasizes backtest orchestration, but the rules disclose an additional destructive action: auto-deleting an existing cloud file (Main.py) and overwriting main.py. Undisclosed destructive side effects are dangerous because users may invoke the skill expecting routine automation while it modifies or deletes project assets beyond what the high-level description suggests.

Content

No source excerpt is available for this finding.

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 · qc_api/config.py (reported line 6)May include surrounding context.

python
def _load_dotenv():
    """Load environment variables from a .env file in the project root (does not overwrite existing vars)."""
    # Search upward from the current file's directory for a .env file
    search_dir = os.path.dirname(os.path.abspath(__file__))
    for _ in range(6):  # Search up to 6 levels

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

python
def _load_dotenv():
    """Load environment variables from a .env file in the project root (does not overwrite existing vars)."""
    # Search upward from the current file's directory for a .env file
    search_dir = os.path.dirname(os.path.abspath(__file__))
    for _ in range(6):  # Search up to 6 levels

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 · qc_api/config.py (reported line 32)May include surrounding context.

python
def _load_dotenv():
    """Load environment variables from a .env file in the project root (does not overwrite existing vars)."""
    # Search upward from the current file's directory for a .env file
    search_dir = os.path.dirname(os.path.abspath(__file__))
    for _ in range(6):  # Search up to 6 levels

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 · qc_api/config.py (reported line 34)May include surrounding context.

python
def _load_dotenv():
    """Load environment variables from a .env file in the project root (does not overwrite existing vars)."""
    # Search upward from the current file's directory for a .env file
    search_dir = os.path.dirname(os.path.abspath(__file__))
    for _ in range(6):  # Search up to 6 levels

Credential Access

High
Category
Privilege Escalation
Confidence
71% confidence
Finding

Opening and parsing the first .env found within six parent directories can cause the tool to ingest credentials or other sensitive settings from an unintended location. In an agent or automation context, this broad search behavior increases the chance of using attacker-controlled or wrong-environment secrets, which could redirect API actions under the wrong account.

Content

Scanner excerpt · qc_api/config.py (reported line 10)May include surrounding context.

python
# Search upward from the current file's directory for a .env file
    search_dir = os.path.dirname(os.path.abspath(__file__))
    for _ in range(6):  # Search up to 6 levels
        env_path = os.path.join(search_dir, ".env")
        if os.path.exists(env_path):
            with open(env_path) as f:
                for line in f:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill advertises and instructs use of environment variables, local file reads/writes, network calls, and shell execution, but it declares no explicit tool scope or permissions boundary. That makes the skill harder to sandbox and review, and increases the chance an agent invokes sensitive capabilities implicitly when handling user requests.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The activation guidance is broad: 'When the user asks to backtest a strategy, run a backtest, or submit code to QuantConnect, follow this workflow.' Without tighter preconditions, an agent could trigger remote code upload, compilation, backtest execution, or file replacement from vague user intent, increasing the risk of unintended cloud actions and modification of a live project.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
95% confidence
Finding

The skill authorizes autonomous deletion of a running backtest based on runtime drawdown thresholds. Because deletion is permanent and the skill states the backtest cannot be recovered, this creates a significant risk of irreversible loss of results or interruption of user work if thresholds are misconfigured, misunderstood, or triggered unexpectedly.

Content

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

2. Monitor with Early-Stop

Poll backtest progress in real time. If drawdown exceeds the user's threshold after 20% progress, automatically delete the backtest to save compute time.

bash
python3 monitor_backtest.py <backtest_id> \

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The method sends arbitrary local file contents to QuantConnect's remote API as part of normal operation, but there is no user-facing confirmation, scoping control, or explicit warning at the call site that strategy source code will be uploaded externally. In a backtest automation skill, this can lead to unintended disclosure of proprietary algorithms, embedded secrets, or sensitive data if callers pass files they did not realize would leave the local environment.

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 · qc_api/config.py (reported line 6)May include surrounding context.

python
def _load_dotenv():
    """Load environment variables from a .env file in the project root (does not overwrite existing vars)."""
    # Search upward from the current file's directory for a .env file
    search_dir = os.path.dirname(os.path.abspath(__file__))
    for _ in range(6):  # Search up to 6 levels

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

python
print(f"\nRunning: {os.path.basename(script_path)} {' '.join(args)}")

    try:
        process = subprocess.Popen(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script reads the specified strategy file and all .json/.txt/.csv files in the same directory, then uploads them to QuantConnect via update_project_files without any explicit warning, confirmation, or allowlist review. In a backtest automation skill, this creates a real risk of unintentionally transmitting proprietary strategies, credentials embedded in local files, or sensitive datasets to a remote service.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The dependency is specified as requests>=2.28.0, which allows installation of any newer version without ensuring reproducibility or constraining to a reviewed release. This increases supply-chain risk and makes builds non-deterministic, so a future vulnerable or breaking version could be pulled into the backtest pipeline unnoticed.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
requests>=2.28.0

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding

Because the manifest does not pin requests to an exact version, it is not possible to verify whether the installed release includes fixes for known advisories affecting older versions. In an automated pipeline that likely performs network requests, an unsafe resolved version could expose credentials, response handling, or transport-related weaknesses depending on runtime resolution.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.