Back to skill

Security audit

Questrade

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Questrade API skill, but it needs review because it can submit or cancel brokerage orders with partner credentials and its safeguards for financial write actions are weak.

Install only if you understand that this skill can access sensitive Questrade account data and, with partner-level credentials, may place or cancel real orders. Keep QUESTRADE_READ_ONLY=true unless you intentionally need trading access, avoid using --force for live accounts, protect the ~/.openclaw credential files, and prefer a pinned dependency set.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/questrade_cli.py:154
Finding

OAuth token files are written without explicitly restrictive permissions

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/questrade_cli.py:80
Finding

Trading protections fail open and can be bypassed with the force option

Content
View full analysis
bool: if os.environ.get("QUESTRADE_READ_ONLY", "").lower() == "true": return True return _load_config().get("readOnly", False) ``` New credential files also explicitly receive a write-enabled default: ```python if "readOnly" not in config: config["readOnly"] = False ``` The order command only blocks trading when read-only mode has already been explicitly enabled: ```python if is_read_only(): print("🔒 Read-only mode is enabled — order placement is blocked.") print(" To allow trades, set QUESTRADE_READ_ONLY=false or remove") print(' "readOnly": true from ~/.openclaw/credentials/questrade.json') sys.exit(1) ``` Price warnings and final confirmation are skipped when `--force` is supplied: ```python if o_type in ("Limit", "StopLimit") and args.limit_price: if side == "Buy" and ask > 0 and args.limit_price > ask and not args.force: print(f"⚠️ Warning: Buy limit ${args.limit_price:.4f} is ABOVE current ask ${ask:.4f}") if input(" Proceed anyway? (y/n): ").strip().lower() != "y": print("Order cancelled.") return elif side == "Sell" and bid > 0 and args.limit_price < bid and not args.force: print(f"⚠️ Warning: Sell limit ${args.limit_price:.4f} is BELOW current bid ${bid:.4f}") if input(" Proceed anyway? (y/n): ").strip().lower() != "y": print("Order cancelled.") return ``` ```python if not args.force: if input(" Confirm order? (y/n): ").strip().lower() != "y": print("Order ...[truncated 2615 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
requirements.txt:2
Finding

Third-party dependency is not reproducibly pinned

Content
View full analysis
=2.28.0 ``` ### Technical Analysis The dependency declaration accepts any current or future release of `requests` newer than version 2.28.0. No lock file or package hash is included. Consequently, separate installations of the same Skill version can resolve to different dependency versions. No evidence of typosquatting, dependency confusion, or a currently malicious package was found; `requests` is a legitimate dependency obtained through normal Python package resolution. The weakness is the lack of reproducibility and integrity enforcement. A future compromised, malicious, or incompatible release could be installed without a corresponding change to the audited project. ### Attack Path 1. An operator installs dependencies using `pip install -r requirements.txt`. 2. Package resolution selects the newest available version satisfying `requests>=2.28.0`. 3. A future compromised release, malicious package-index response, or incompatible version is selected. 4. Package installation or subsequent import executes or loads the affected dependency under the user's privileges. 5. Because the CLI handles OAuth tokens and authenticated API traffic, a compromised dependency could access credentials, alter requests, or transmit sensitive account data. ### Impact Assessment The dependency executes with the same operating-system privileges as the CLI and is directly involved in all authentication and API traffic. A compromised package could read the local token files, capture bearer and refresh tokens, modify Questrade transactions, or access any other data available to the invoking user. The practical likelihood is lower than the other findings because exploitation depends on a compromised or unsafe package release or package source; no such active com ...[truncated 54 chars]
Remediation
View remediation
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (18)

Tainted flow: 'refresh_token' from os.environ.get (line 124, credential/environment) → requests.post (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 · scripts/questrade_cli.py (reported line 184)May include surrounding context.

python
# Questrade docs show this as a GET with query params; POST with params= is
    # equivalent (params become the query string either way) and aligns with
    # RFC 6749 which recommends POST for token endpoints.
    resp = requests.post(
        login_url,
        params={"grant_type": "refresh_token", "refresh_token": refresh_token},
        timeout=15,

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

text

> **Important:** Questrade refresh tokens *rotate* — every time the token is
> used to obtain a new access token, Questrade issues a new refresh token.
> This script automatically saves the new refresh token back to the credentials
> file, so you only need to paste the initial token once.

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

python
```

> **Important:** Questrade refresh tokens *rotate* — every time the token is
> used to obtain a new access token, Questrade issues a new refresh token.
> This script automatically saves the new refresh token back to the credentials
> file, so you only need to paste the initial token once.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
83% confidence
Finding

The skill includes support for order cancellation and placement against brokerage endpoints, which are high-risk state-changing operations in a financial context. Although the documentation notes personal tokens cannot use these endpoints, the capability becomes dangerous if partner credentials are present, since prompt abuse or operator error could trigger destructive trading actions.

Content

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

md
## Safety Notes

- **Personal API tokens cannot place trades.** Questrade restricts order placement
  and cancellation (`POST`/`DELETE /accounts/{id}/orders`) to partner-level API
  access only. Attempting either with a personal token returns `403 Forbidden`.
- The `order` and `cancel-order` commands are included for completeness but will
  not work unless you have Questrade partner API access.

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

md
- The `order` and `cancel-order` commands are included for completeness but will
  not work unless you have Questrade partner API access.
- The refresh token rotates on every use; do **not** share the credentials file.
- Access tokens expire in ~30 minutes; the script caches and refreshes them automatically.

Tool Parameter Abuse

High
Category
Tool Misuse
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).

Content

Scanner excerpt · references/api.md (reported line 289)May include surrounding context.

md
---

### DELETE /v1/accounts/{id}/orders/{orderId} *(partner access)*

Cancel an order. Returns updated order detail.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
97% confidence
Finding

The explicit rm ~/.openclaw/data/questrade-token-cache.json command is a direct local file-deletion action that could be executed by a user or agent without understanding its consequences. In a skill context, this is dangerous because it mixes executable host-level remediation with passive API documentation, creating a path for unintended local state manipulation.

Content

Scanner excerpt · references/api.md (reported line 543)May include surrounding context.

Delete the token cache file and retry — the CLI will refresh automatically:

bash
rm ~/.openclaw/data/questrade-token-cache.json

If the refresh token itself is expired (7-day window), generate a new one from

Credential Access

High
Category
Privilege Escalation
Confidence
84% confidence
Finding

The script caches live access tokens to a JSON file on disk, and no permission hardening is visible around file creation. Access tokens grant API access for their lifetime, so if another local user, process, backup system, or malware can read the file, they may gain temporary unauthorized access to account and trading data, and potentially trading actions depending on scope.

Content

Scanner excerpt · scripts/questrade_cli.py (reported line 202)May include surrounding context.

python
# Questrade refresh tokens rotate — save the new one
    _save_refresh_token(new_refresh, practice)

    # Cache access token (subtract 60 s as buffer)
    TOKEN_CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
    expires_at = datetime.now(timezone.utc) + timedelta(seconds=max(expires_in - 60, 0))
    with open(TOKEN_CACHE_FILE, "w") as f:

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
86% confidence
Finding

The skill documents a --force flag that bypasses interactive confirmation for order placement, enabling fully autonomous trade execution if the underlying account has partner-level trading permissions. In a financial skill, removing human approval on a state-changing action materially increases the risk of unintended or prompt-induced transactions.

Content

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

Stop-limit order

python3 scripts/questrade_cli.py order sell 12345678 8049 5 StopLimit --stop-price 140.00 --limit-price 139.50

Skip confirmation prompts

python3 scripts/questrade_cli.py order buy 12345678 8049 10 Market --force

text

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The reference documents order placement and cancellation endpoints without a prominent warning that these actions can affect real brokerage accounts and financial positions. In an agent or automation context, omission of that warning increases the chance of accidental live trading or cancellation when users assume the examples are informational only.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The document includes a destructive local shell command to delete an authentication cache file, which is unrelated to the remote API specification itself and encourages users or downstream agents to modify local state. In an agent setting, copying such remediation text into automated execution paths could cause unintended deletion of credentials or authentication state on the host machine.

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

python
def _invalidate_config_cache() -> None:
    """Clear the config cache after a write so the next read is fresh."""
    global _config_cache
    _config_cache = None

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script automatically persists rotated refresh tokens to disk without an explicit user-facing warning or consent at write time. Because refresh tokens are long-lived credentials, silent storage increases the risk of credential theft from local compromise, backups, shared home directories, or permissive file permissions, especially in an agent-skill environment that may run unattended.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/questrade_cli.py (reported line 234)May include surrounding context.

python
def api_post(path: str, body: Optional[dict] = None) -> dict:
    base, headers = _auth()
    resp = requests.post(
        f"{base}{path}",
        headers={**headers, "Content-Type": "application/json"},
        json=body,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Order cancellation is a destructive financial action, yet the cancel-order command executes immediately once invoked, with no confirmation prompt, dry-run mode, or explicit warning. In an agent or automation context, a mistaken command, argument mix-up, or prompt-injection-driven invocation could cancel legitimate open orders and materially affect trading outcomes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The token-cache deletion instruction omits a warning that it removes local authentication state and may disrupt access until credentials are refreshed. While not inherently malicious, this can mislead users or agents into performing unnecessary destructive local actions during routine error handling.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency specification uses a lower-bound range (requests>=2.28.0) instead of pinning to an exact version, which makes builds non-reproducible and can unexpectedly pull in different package versions over time. In a security context this increases supply-chain risk and makes it harder to verify whether known-vulnerable or regressed releases are being installed.

Content

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

text
# Python 3.9+ required (uses zoneinfo, f-strings, typing.Optional)
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
90% confidence
Finding

Because requests is not pinned, the manifest does not prove which concrete release will be installed, so you cannot determine whether deployment will include a version affected by one of the known advisories. This is dangerous primarily as an uncertainty and supply-chain hygiene issue: depending on environment and install time, a vulnerable version could be selected without visibility.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.