Back to skill

Security audit

Backtest Poller

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent with its stated QuantConnect monitoring purpose, but its background poller contains unsafe paths that can run shell commands, write outside the results folder, load unverified code, and automatically delete backtests.

Review before installing. This skill needs QuantConnect API credentials and runs a background poller that can permanently delete backtests when drawdown thresholds are crossed. It should be fixed to remove shell-based notifications, sanitize result paths, pin dependencies, and require explicit opt-in or confirmation for early-stop deletion and auto-diagnosis plugin loading.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
poller.py:541
Finding

Shell Command Injection Through Backtest Notification Name

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
poller.py:375
Finding

Path Traversal and Arbitrary File Writes in Automatic Result Download

Content
View full analysis
Remediation
View remediation

T07 · Tool Hijacking and Spoofing

Error
Location
poller.py:481
Finding

Automatic Execution of Unverified Modules from the Python Search Path

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Unbounded and Non-Reproducible Third-Party Dependencies

Content
View full analysis
=2.28.0 python-dotenv>=1.0.0 ``` ### Technical Analysis The dependency declarations specify only minimum versions. A future installation may therefore resolve to any newer release accepted by the package resolver. No lock file, upper version boundary, or package hash is supplied. This makes builds non-reproducible and permits dependency behavior to change after the skill has been reviewed. If an upstream account or release process is compromised, or a future incompatible release is published, rebuilding the environment can install unreviewed code. Python packages execute code during import, and installation tooling may also process package build metadata. ### Attack Path 1. The project is installed or rebuilt at a later time. 2. The package resolver selects the newest versions satisfying the lower bounds. 3. A compromised, malicious, or incompatible release is downloaded from the configured package index. 4. The package is installed without hash verification. 5. Its code executes when imported by the CLI or daemon. ### Impact Assessment A compromised dependency executes with the permissions of the user installing or running the skill. It may access QuantConnect credentials, state files, backtest results, and network resources. The practical likelihood depends on the security of the configured package index and upstream projects, but the absence of version and integrity controls increases supply-chain risk. ]]>
Remediation
View remediation
Vulnerability Patterns
  • 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
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Rogue AgentSelf-Modification, Session Persistence
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill markets itself as a monitoring daemon, but the documented behavior includes a destructive early-stop that permanently deletes backtests. A description-behavior mismatch is dangerous because users may consent to passive monitoring while unknowingly granting authority for destructive API actions and persistent background execution.

Content

No source excerpt is available for this finding.

Memory Manipulation

High
Category
Memory Poisoning
Confidence
90% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

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

md
## Rules

- **Never submit more than one backtest at a time.** QuantConnect free tier only allows one concurrent backtest per project. Submitting a second will silently queue or fail.
- **Never manually edit `state.json`** while the poller is running. Use the CLI commands (`submit`, `clear`) to modify state. The file uses `fcntl.flock` locking — manual edits bypass the lock and can corrupt state.
- **Never run multiple poller instances simultaneously.** The CLI checks for an existing PID before starting. If you force-start a second poller, both will race on state.json writes.
- **Do not delete files in `results/`** while the poller is actively downloading. Wait for the "completed" status.
- **Early-stop deletes the backtest permanently** from QuantConnect. There is no "pause" API — early-stopped backtests cannot be resumed or recovered.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
97% confidence
Finding

The notification code builds a shell command with f-string interpolation and passes attacker-influenced data (bt.name) into os.system(). If a backtest name contains shell metacharacters or quoting payloads, this can lead to arbitrary command execution under the poller process. The daemon context makes this more dangerous because it runs unattended and may execute later without direct user visibility.

Content

Scanner excerpt · poller.py (reported line 544)May include surrounding context.

python
try:
            title = "Backtest Poller"
            message = f"Backtest {bt.name} finished ({bt.status})"
            os.system(
                f"""osascript -e 'display notification "{message}" with title "{title}"'"""
            )
            logger.info(f"Notification sent: {message}")

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_client.py (reported line 42)May include surrounding context.

python
if not user_id or not api_token:
        raise EnvironmentError(
            "QC_USER_ID and QC_API_TOKEN environment variables are required. "
            "Set them in your shell or in a .env file."
        )
    return user_id, api_token

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares required environment variables and describes capabilities that inherently use shell execution, network access, and file read/write, but it does not declare any explicit tool scope or permissions boundary. That omission weakens reviewability and least-privilege controls, making it easier for an agent or operator to invoke broader capabilities than users may expect.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
65% 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 · cli.py (reported line 9)May include surrounding context.

python
later to check results.

Architecture:
  1. submit:       Submit a backtest -> register in state.json -> start nohup poller
  2. status:       View all backtest progress (reads state.json)
  3. logs:         View poller daemon logs
  4. results:      View completed backtest results and diagnostics

Session Persistence

Medium
Category
Rogue Agent
Confidence
65% 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 · poller.py (reported line 18)May include surrounding context.

python
later to check results.

Architecture:
  1. submit:       Submit a backtest -> register in state.json -> start nohup poller
  2. status:       View all backtest progress (reads state.json)
  3. logs:         View poller daemon logs
  4. results:      View completed backtest results and diagnostics

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

python
)

    log_fd = open(LOG_FILE, "a")
    process = subprocess.Popen(
        [sys.executable, poller_script],
        stdout=log_fd,
        stderr=log_fd,

Tainted flow: 'json_path' from os.environ.get (line 383, credential/environment) → open (file write)

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

python
# 1. Save full backtest result as JSON
            resp = self.client.read_backtest(bt.project_id, bt.backtest_id)
            json_path = os.path.join(result_dir, f"{bt.backtest_id}_result.json")
            with open(json_path, "w") as f:
                json.dump(resp, f, indent=2)

            # 2. Download orders in batches

Tainted flow: 'csv_path' from os.environ.get (line 415, credential/environment) → open (file write)

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

python
csv_path = os.path.join(result_dir, f"{bt.backtest_id}_orders.csv")
            if all_orders:
                with open(csv_path, "w", newline="") as csvfile:
                    fieldnames = [
                        "orderId", "symbol", "type", "direction", "quantity",
                        "fillPrice", "status", "submitTime", "fillTime", "tag",

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

The code dynamically imports order_forensics or forensics from whatever appears on the Python path, then executes its methods. This creates a code execution hook via module hijacking/path poisoning if an attacker can place a malicious module earlier on sys.path, especially in shared or writable working directories. In a long-running daemon that auto-diagnoses results, this expands the trust boundary beyond the core polling function.

Content

Scanner excerpt · poller.py (reported line 488)May include surrounding context.

python
"forensics",
            ]:
                try:
                    mod = __import__(module_path, fromlist=["OrderForensics"])
                    OrderForensics = getattr(mod, "OrderForensics", None)
                    if OrderForensics:
                        break

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill introduces subprocess/shell execution for notifications, which is outside the minimum capability needed for polling. Because that subprocess path is implemented through os.system() with interpolated data, it materially increases attack surface and can be abused for command execution. The background-daemon behavior makes misuse less visible and therefore more dangerous.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The code invokes os.system(...) to execute an AppleScript command, which is a subprocess/shell operation. While there is internal logging after the fact, the file does not provide a user-facing warning or confirmation around this execution, and shell execution is one of the operations that should be disclosed.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The client includes a destructive capability (delete_backtest) even though the skill is described primarily as a monitoring/background polling tool. In an agent setting, hidden or unnecessary write/destructive actions expand the blast radius: a compromised workflow, prompt injection elsewhere, or accidental invocation could abort or delete user backtests contrary to user expectations.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

The dependency is specified with a lower bound only (requests>=2.28.0), which makes builds non-reproducible and allows future installs to resolve to unexpected versions, including versions later found vulnerable or behaviorally incompatible. In a background polling daemon that likely performs network access and may handle credentials or API tokens, this increases supply-chain and patch-verification risk.

Content

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

text
requests>=2.28.0
python-dotenv>=1.0.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
92% confidence
Finding

The manifest does not pin requests, and the package has multiple known advisories across versions, so there is no way to verify from this file whether deployment will avoid affected releases. Since this skill is a long-running daemon that likely polls remote services and may use authentication, unresolved requests vulnerabilities could expose credentials, weaken TLS/request safety, or leak sensitive information depending on the resolved version.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

python-dotenv>=1.0.0 is also unpinned, so installations may pull different versions over time and make it impossible to verify whether a deployed environment includes a safe, reviewed release. Because this package influences environment-variable loading, version drift can affect secrets handling and exposure risks.

Content

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

text
requests>=2.28.0
python-dotenv>=1.0.0

Unverifiable Dependency: python-dotenv has 2 known advisory(ies) (CVE-2026-28684 (python-dotenv: Symlink following in set_key allows arbitrary file overwrite via ); CVE-2026-28684 (python-dotenv reads key-value pairs from a .env file and can set them as environ)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
86% confidence
Finding

python-dotenv has known advisories in some versions, and because the dependency is not pinned, the manifest does not demonstrate that installs will avoid affected releases. In software that likely reads environment configuration for API access, a vulnerable dotenv version could contribute to unsafe file handling or secrets-management issues if the affected code paths are used.

Content

No source excerpt is available for this finding.