Back to skill

Security audit

trading212 Skill

Security checks for vulnerabilities and agentic risk

Overview

This Trading212 skill is mostly coherent with its stated purpose, but it can submit real-money trades and uses broad external configuration/storage paths without strong runtime safeguards.

Install only if you are comfortable giving this skill access to your Trading212 account data and potential trading authority. Keep TRADING212_DEMO=true unless you intend real-money trading, use restricted/read-only credentials where possible, avoid storing secrets in shared parent .env files, and review or relocate the snapshot and rules paths before use.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/trading212_skill.py:30
Finding
Shared Parent Environment File Can Redirect Operations to Live Trading<![CDATA[ ## Vulnerability Details **File Location**: `scripts/trading212_skill.py:30-34` **Vulnerability Type**: Unsafe configuration loading from a shared parent directory **Risk Level**: High ### Vulnerable Code ```python # Load .env from project root before any Trading212 imports. _env_path = Path(__file__).resolve().parents[3] / ".env" if _env_path.exists(): from dotenv import load_dotenv load_dotenv(_env_path) ``` ### Technical Analysis The code claims to load `.env` from the project root, but `Path(__file__).resolve().parents[3]` resolves to `/tmp` in the audited directory layout. Consequently, the Skill implicitly trusts `/tmp/.env`, a file outside the project and potentially shared with unrelated users or processes. `load_dotenv()` does not override variables already present by default, but it supplies variables that are absent from the process environment. This includes `TRADING212_DEMO`, which controls whether the client connects to the demo or live Trading212 API. The behavior exceeds the minimum privilege and trust boundary required for the Skill. Configuration should come from the Skill directory or explicitly supplied environment variables, not a shared parent directory. ### Attack Path 1. An attacker with local write access creates `/tmp/.env`. 2. The file includes: ```text TRADING212_DEMO=false ``` 3. A user or agent starts the Skill without explicitly setting `TRADING212_DEMO`. 4. The Skill loads the attacker-controlled `/tmp/.env`. 5. `Trading212Client` interprets the value as live-trading mode. 6. A subsequent `execute_trade` invocation submits an order to the live Trading212 endpoint. Exploitation still requires valid Trading212 credentials to be available, but the attacker-controlled file can silently change the environment selected for those credentials. ### Impact Assessment The flaw can redirect operations from paper trading to real-money trading. In combination with the unguarded execution interface, this can ...[truncated 169 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Resolve the file relative to the actual Skill root: ```python _PROJECT_ROOT = Path(__file__).resolve().parents[1] _env_path = _PROJECT_ROOT / ".env" ``` - Prefer requiring deployment-time environment variables instead of implicitly loading a file. - If `.env` support is retained: - Verify that the resolved path remains under the project root. - Reject symlinks. - Verify file ownership and require restrictive permissions. - Log the selected environment without disclosing credentials. - Require a separate explicit command-line switch for live trading; do not permit `.env` alone to activate it. - Fail closed unless demo mode is positively established. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/proposal_rules.py:24
Finding
Proposal Rules Are Loaded from an Unsafe External Default Path<![CDATA[ ## Vulnerability Details **File Location**: `scripts/proposal_rules.py:24-36` **Vulnerability Type**: External configuration trust and unsafe path resolution **Risk Level**: Medium ### Vulnerable Code ```python _CONFIG_PATH = Path(__file__).resolve().parents[3] / "config" / "rules.yaml" RISK_MULTIPLIERS = { "low": 0.25, "medium": 0.50, "high": 1.00, } def _load_config() -> Dict[str, Any]: path = Path(os.environ.get("TRADING212_RULES_PATH", str(_CONFIG_PATH))) if not path.exists(): return {} with open(path, "r", encoding="utf-8") as fh: data = yaml.safe_load(fh) or {} return data.get("proposal_rules", {}) ``` ### Technical Analysis In the audited layout, `parents[3] / "config" / "rules.yaml"` resolves to `/tmp/config/rules.yaml`, rather than the documented project-local `config/rules.yaml`. As a result, a file in a shared directory can control proposal thresholds, DCA ticker lists, risk mode, stop-loss behavior, cost-averaging settings, and suggested trade quantities. YAML is parsed with `safe_load`, so this is not arbitrary Python code execution; the risk is unauthorized manipulation of financial decision inputs. The implementation also does not validate the loaded configuration against a schema or enforce safe numeric ranges. ### Attack Path 1. An attacker creates `/tmp/config/rules.yaml`. 2. The attacker supplies manipulated proposal settings, such as: ```yaml proposal_rules: risk_mode: high stop_loss_pct: 0 dca_min_cash: 0 dca_amount: 100000 dca_tickers: - "ATTACKER_SELECTED_TICKER" ``` 3. The user or agent invokes proposal mode. 4. The Skill loads the shared file instead of the packaged `config/rules.yaml`. 5. The generated recommendations reflect attacker-selected thresholds and symbols. 6. If the user accepts the recommendations, or another component invokes execution based on them, harmful trades may follow. ### Impact Assessment An attacker can man ...[truncated 241 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Correct the default path: ```python _CONFIG_PATH = Path(__file__).resolve().parents[1] / "config" / "rules.yaml" ``` - Resolve the path canonically and verify it remains within an approved configuration directory. - Reject symlinks and configuration files with unsafe ownership or permissions. - Validate YAML against a strict schema, including: - Enumerated risk modes. - Nonnegative cash and amount values. - Sensible percentage ranges. - Upper limits on proposed transaction values. - Valid ticker syntax and bounded list lengths. - If `TRADING212_RULES_PATH` is supported, require explicit operator configuration and document that it expands the trust boundary. - Display the active configuration source and material rule changes to the user. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/snapshot.py:18
Finding
Sensitive Portfolio Snapshots Use a Shared Temporary Directory Without File Hardening<![CDATA[ ## Vulnerability Details **File Location**: `scripts/snapshot.py:18,31-39,91-94` **Vulnerability Type**: Insecure temporary-file storage and sensitive local data exposure **Risk Level**: Medium ### Vulnerable Code ```python # Default directory: <project_root>/snapshots/ _DEFAULT_DIR = Path(__file__).resolve().parents[3] / "snapshots" ``` ```python def _snapshot_dir() -> Path: """Return the snapshot directory, creating it if needed.""" d = Path(os.environ.get("TRADING212_SNAPSHOT_DIR", str(_DEFAULT_DIR))) d.mkdir(parents=True, exist_ok=True) return d def _path_for_date(date_str: str) -> Path: return _snapshot_dir() / f"{date_str}.json" ``` ```python fp = _path_for_date(date_str) with open(fp, "w", encoding="utf-8") as fh: json.dump(snapshot, fh, indent=2, ensure_ascii=False) return fp ``` The stored object includes sensitive financial information: ```python snapshot: Dict[str, Any] = { "date": date_str, "total_value": round(total_value, 2), "cash": round(cash, 2), "positions": [ { "ticker": p["ticker"], "value": round(p["value"], 2), "quantity": p["quantity"], "avg_price": round(p.get("avg_price", 0), 4) if p.get("avg_price") else None, "current_price": round(p.get("current_price", 0), 4) if p.get("current_price") else None, } for p in positions ], } ``` ### Technical Analysis The default snapshot directory resolves to `/tmp/snapshots`, not a private project or user-data directory. Snapshot names are predictable dates, and files are opened through a normal path without: - Explicit `0600` permissions. - Directory ownership verification. - Symlink rejection. - Exclusive creation or safe replacement. - Atomic writes. The files disclose total portfolio value, cash, tickers, quantities, average acquisition prices, and current values. Existing snapshots are also trusted as inputs for performance and proposal calculations. ...[truncated 1078 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Store snapshots in a user-private application-data directory rather than `/tmp`. - Correct the project-local fallback if project storage is intended: ```python _DEFAULT_DIR = Path(__file__).resolve().parents[1] / "snapshots" ``` - Create the directory with mode `0700` and verify ownership. - Create snapshot files with mode `0600`. - Reject symbolic links using `lstat()` checks and platform-appropriate no-follow flags. - Write to a securely created temporary file in the same private directory, flush and synchronize it, then atomically replace the destination. - Validate loaded snapshot structure, types, date, and reasonable numeric ranges. - Consider integrity protection if snapshots influence financially significant recommendations. - Document retention behavior and provide a secure deletion option. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/trading212_skill.py:350
Finding
Real-Money Orders Can Be Submitted Without a Code-Enforced Confirmation Boundary<![CDATA[ ## Vulnerability Details **File Location**: `scripts/trading212_skill.py:350-368,806-870` **Vulnerability Type**: Missing authorization and confirmation control for sensitive transactions **Risk Level**: High ### Vulnerable Code The execution function directly submits an order after parameter validation: ```python # Trading212 convention: sell = negative quantity. signed_qty = float(quantity) if side == "buy" else -float(quantity) try: if order_type == "limit": if limit_price is None: _error("limit_price is required for limit orders") resp = client.place_limit_order( ticker=symbol, quantity=signed_qty, limit_price=float(limit_price), ) else: resp = client.place_market_order(ticker=symbol, quantity=signed_qty) ``` The command dispatcher invokes this function immediately: ```python elif args.mode == "execute_trade": if not args.params: _error("--params is required for execute_trade mode") try: params = json.loads(args.params) except json.JSONDecodeError as exc: _error(f"Invalid JSON in --params: {exc}") _run_execute_trade(client, params) ``` The documentation requires explicit user confirmation, but no corresponding runtime proof is checked. ### Technical Analysis The Skill treats possession of command-line access and API credentials as sufficient authorization to place an order. It does not enforce: - A two-phase prepare-and-confirm workflow. - A confirmation token tied to exact order details. - A second explicit live-trading acknowledgement. - Maximum order quantity or monetary-value limits. - Expiration of previously approved order details. Prompt-level instructions telling the agent to ask for confirmation are not equivalent to a code-level security boundary. They can be bypassed by direct invocation, integration mistakes, or an agent that fails to follow the documented sequence. Pre-trade cash and position check ...[truncated 1229 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Implement a two-phase transaction protocol: 1. Prepare and display the exact order. 2. Issue a short-lived, cryptographically protected confirmation token. 3. Require a separate invocation containing that token. - Bind the token to: - Account environment. - Symbol. - Side. - Quantity. - Order type. - Limit price. - Expiration timestamp. - Require a separate explicit live-trading switch that cannot be activated solely through an implicitly loaded `.env` file. - Add configurable per-order and daily monetary limits. - Fail closed when cash or position validation cannot be completed. - Re-fetch and display the selected environment and final order details immediately before submission. - Consider using separate read-only and trading API credentials, with trading credentials unavailable to analysis-only modes. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Dependency Installation Is Not Reproducible or Integrity-Pinned<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-3`; installation instruction at `SKILL.md:18-22` **Vulnerability Type**: Mutable third-party dependency resolution **Risk Level**: Low ### Vulnerable Code ```text requests>=2.31.0 python-dotenv>=1.0.0 pyyaml>=6.0.1 ``` The documented installation command is: ```bash pip install -r {baseDir}/requirements.txt ``` ### Technical Analysis The package names are established dependencies, and the audit found no evidence of typosquatting, dependency confusion, or a malicious package. However, open-ended `>=` constraints allow future releases to be selected automatically, and no package hashes are supplied. This makes installation non-reproducible and leaves package authenticity dependent on the configured package index and TLS trust. A future compromised release, malicious mirror, or incompatible version could introduce code that executes during installation or runtime. ### Attack Path 1. An operator follows the documented `pip install` command. 2. The resolver selects any available package version satisfying the lower bound. 3. A compromised package index, mirror, account, or future matching release supplies altered content. 4. The package is installed into the Skill environment. 5. Installation hooks or imported package code execute with the privileges of the installer or Skill process. No evidence indicates that this attack is presently occurring; this is a supply-chain hardening weakness. ### Impact Assessment A compromised dependency could access the Skill process environment, including Trading212 credentials, portfolio data, and network access. If installation is performed with elevated privileges, the impact could extend to that installation context. Actual scope depends on the package source and installer privileges. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Pin reviewed exact versions instead of using open-ended lower bounds. - Generate and verify cryptographic hashes: ```bash pip install --require-hashes -r requirements.txt ``` - Use a lock file produced from a reviewed dependency-resolution process. - Install from an explicitly trusted package index. - Run dependency vulnerability and provenance checks in CI. - Perform installation in an isolated virtual environment without unnecessary privileges. - Establish a controlled update process that reviews and tests dependency changes before deployment. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (25)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill claims broad portfolio-analysis and safety-aware trading behavior that the underlying implementation reportedly does not actually provide, while still exposing API/client and order-placement functionality. In a financial context, this mismatch can mislead users and supervising systems into trusting nonexistent analysis, validation, or guardrails before trades are proposed or executed.

Missing User Warnings

High
Confidence
95% confidence
Finding
The client exposes direct order-placement primitives for market and limit orders, and the skill description explicitly says it can make trade proposals and place orders. In an agent setting, the absence of an explicit confirmation/approval gate in the action path creates a real risk of unintended or prompt-induced live trades, especially when `demo` can be switched to live via environment configuration.

Credential Access

High
Category
Privilege Escalation
Content
from datetime import datetime, timedelta, timezone
from pathlib import Path

# Load .env from project root before any Trading212 imports.
_env_path = Path(__file__).resolve().parents[3] / ".env"
if _env_path.exists():
    from dotenv import load_dotenv
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
from pathlib import Path

# Load .env from project root before any Trading212 imports.
_env_path = Path(__file__).resolve().parents[3] / ".env"
if _env_path.exists():
    from dotenv import load_dotenv
    load_dotenv(_env_path)
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill can place live market or limit orders directly from parsed parameters without any built-in confirmation gate, dry-run default, or explicit safety interlock for live accounts. In an agent setting, mis-parsed prompts, prompt injection from upstream context, or accidental invocation could trigger irreversible financial transactions and losses.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises and instructs use of capabilities that imply access to environment secrets, local files, and outbound network requests, but it does not declare any explicit tool scope or permission boundary. For a trading skill that can place orders and uses API credentials, this increases the chance of over-broad execution, secret exposure, or unintended network/file actions without transparent restriction.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The top-level description uses broad invocation language like portfolio overview, daily performance, or trade actions, which can match common finance-related requests too easily. This can cause the skill to activate in contexts where the user did not specifically intend Trading212 access, increasing the risk of unnecessary account-data exposure or escalation toward trade workflows.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The file instructs the agent to present output as a readable English summary, which imposes a language choice without user opt-in. The same policy appears again in the output format section, and no alternative language handling or justification is provided.

Vague Triggers

Medium
Confidence
96% confidence
Finding
Example trigger phrases such as 'What should I do?' or 'Any trade suggestions?' are highly ambiguous and could be triggered by everyday conversation about markets rather than an explicit request to use this brokerage skill. In a trade-capable skill, accidental activation is more dangerous because it may expose holdings or steer the interaction toward order execution.

Natural-Language Policy Violations

Medium
Confidence
99% confidence
Finding
The instruction to present a human-readable English summary is a natural-language policy issue because it hard-codes a specific language for all users. There is no opt-in, locale selection, or documented rationale for restricting responses to English.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This reference explicitly documents a mode that can place orders and also states that snapshots are automatically written to disk, but it does not prominently warn about the real-world side effects of those actions. In a trading skill, unclear safety boundaries can lead to unintended financial transactions or silent local data persistence, especially if the caller assumes the skill is read-only or demo-only.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This file generates concrete buy/sell/reduce proposals, including quantities and rationales, but does not attach any explicit warning that the output is informational and may lead to financially impactful actions if acted on or passed to downstream order-placement components. In the context of a Trading212 skill that can also place orders, this omission increases the risk that users or automation treat these proposals as safe, authoritative trading instructions without adequate confirmation or suitability checks.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
`cancel_order` performs a destructive account action immediately with no built-in confirmation or secondary authorization. In a trading agent context, accidental, coerced, or ambiguous cancellation requests could interfere with user strategy, remove protective orders, or cause financial loss through missed execution.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
Multiple user-facing messages are emitted in Dutch, including trade descriptions, validation errors, watchlist alerts, and allocation guidance. This forces a specific language/locale without any opt-in or documented justification, which matches the language/locale policy violation criteria.

Vague Triggers

Low
Confidence
82% confidence
Finding
Across multiple mode descriptions, the skill repeatedly uses broad trigger examples that are not specific to Trading212, which raises the likelihood of unintentional routing. While largely a prompt-quality issue, the context makes it security-relevant because the skill can access financial data and eventually place orders.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
Several user-facing message examples and descriptions are written in Dutch, such as trade event descriptions and validation errors, while the rest of the reference is in English. Because the file does not state that the skill is Dutch-only or provide language selection, it appears to impose a locale on output without user opt-in.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
Alert messages and rebalance reasons in this section are also presented in Dutch, reinforcing a fixed-language behavior in otherwise English documentation. The file does not indicate that users can choose their preferred language or that the locale restriction is intentional and justified.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
python-dotenv>=1.0.0
pyyaml>=6.0.1
Confidence
96% confidence
Finding
The dependency is specified with a lower bound only, which allows future installs to resolve to different versions over time and weakens reproducibility and supply-chain control. In a skill that can access portfolio data and place trades, unexpected dependency changes can increase operational and security risk if a newly resolved version is vulnerable or malicious.

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
85% confidence
Finding
The manifest does not pin requests, so the actual installed version cannot be compared reliably against known advisories. In a network-facing trading skill, an unknowingly vulnerable HTTP client could expose credentials, weaken TLS/request handling, or leak sensitive information depending on the resolved version.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
python-dotenv>=1.0.0
pyyaml>=6.0.1
Confidence
95% confidence
Finding
Using python-dotenv with only a minimum version permits non-deterministic installs and makes it difficult to verify which release is deployed. This is a supply-chain hygiene issue that becomes more important in a financial-trading skill because environment handling may include sensitive API credentials.

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
83% confidence
Finding
Because python-dotenv is unpinned, it is not possible to verify whether deployments are using a version affected by known advisories. This matters in a skill likely to use environment variables for brokerage API keys, where a vulnerable release could mishandle or expose sensitive configuration.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
python-dotenv>=1.0.0
pyyaml>=6.0.1
Confidence
96% confidence
Finding
An unpinned PyYAML dependency means builds may silently consume different releases, reducing reproducibility and making it harder to guarantee that only vetted versions are used. Because PyYAML has a history of security issues, this raises the chance of accidentally deploying an affected version.

Unverifiable Dependency: pyyaml has 8 known advisory(ies) (CVE-2019-20477 (Deserialization of Untrusted Data in PyYAML); CVE-2020-1747 (Improper Input Validation in PyYAML); CVE-2020-14343 (Improper Input Validation in PyYAML) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
88% confidence
Finding
The unpinned pyyaml dependency is especially concerning because PyYAML has multiple historical deserialization and input-validation advisories, and the manifest provides no assurance that a safe version will be installed. If this skill parses YAML from untrusted or semi-trusted sources, a vulnerable version could increase the risk of code execution or other unsafe parsing outcomes.

Natural-Language Policy Violations

Low
Confidence
98% confidence
Finding
The `_ERROR_MESSAGES` mapping contains fixed Dutch strings for common failures, and additional error text elsewhere also uses Dutch. This creates a language/locale policy issue because the skill does not offer a language choice or document that it is intentionally Dutch-only.

Context-Inappropriate Capability

Low
Confidence
85% confidence
Finding
The manifest describes portfolio analysis, trade proposals, watchlist, history, dividends, allocation, and order placement. This file additionally reaches outside the skill directory to load a global .env file from the project root, which is a broader credential/config access capability not mentioned in the skill’s stated purpose.

Static analysis

No suspicious patterns detected.