Back to skill

Security audit

Tiger Trade

Security checks for vulnerabilities and agentic risk

Overview

This trading skill is purpose-aligned, but it provides unsafe live-trading instructions that could cause accidental real-money orders and exposes brokerage credentials in a local plaintext file.

Review carefully before installing. This skill can operate on a live brokerage account, and the sample code is not a safe dry run. Do not use it unless you are prepared to add sandbox defaults, explicit order review and confirmation, trade limits, contract verification, and secure credential storage with restrictive permissions or a secret manager.

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

Error
Location
SKILL.md:12
Finding

Private Trading Key Stored and Loaded as Plaintext

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 12–18 and 40–46
Vulnerability Type: Plaintext sensitive credential storage
Risk Level: High

The skill instructs users to place a Tiger Brokers private key directly in a plaintext JSON file:

json
{
  "tiger_id": "your_tiger_id",
  "account": "your_account",
  "private_key_pk8": "your_private_key"
}

It then reads that credential from the filesystem and assigns it directly to the live trading client:

python
with open('~/.tiger-config.json', 'r') as f:
    config = json.load(f)

client_config = TigerOpenClientConfig()
client_config.tiger_id = config['tiger_id']
client_config.account = config['account']
client_config.private_key = config['private_key_pk8']

Technical Analysis

The documented configuration design stores a private authentication key without encryption, access-control validation, or secret-manager integration. Any process or user capable of reading the configuration file can recover the Tiger Brokers account identifier and private key.

The instructions also do not require restrictive file permissions such as mode 0600. Consequently, an insecure umask, accidental backup, synchronization utility, diagnostic collection process, or local information-disclosure flaw could expose the credential.

In addition, Python does not expand ~ automatically in open('~/.tiger-config.json', ...). Although this commonly causes a file-not-found error, a pre-existing attacker-controlled directory named ~ under the working directory could cause the program to load an unintended configuration. This should not be relied upon as the primary exploit path, but it further weakens configuration integrity.

Attack Path

  1. A user follows the setup instructions and writes the Tiger Brokers private key to ~/.tiger-config.json.
  2. The file is created with permissions that permit access by another local principal, or it is exp ...[truncated 853 chars]
Remediation
View remediation

Remediation Suggestions

  • Store the private key in an operating-system keychain, hardware-backed secret store, or dedicated secrets manager rather than embedding it in JSON.
  • If a local file is unavoidable, keep the private key in a separate file encrypted at rest and enforce owner-only permissions (0600) and a private parent directory (0700).
  • Resolve the configuration path safely with pathlib.Path.home() or os.path.expanduser() rather than passing a literal ~ to open().
  • Verify that the resolved configuration path is a regular file owned by the expected user and is not a symbolic link.
  • Validate file permissions before loading credentials and refuse to continue if group or world access is enabled.
  • Avoid printing, logging, or returning the private key in exceptions or diagnostic output.
  • Support credential rotation and document immediate revocation procedures for suspected exposure.
  • Prefer scoped credentials and broker-side controls that restrict account access, order value, eligible instruments, and withdrawal capabilities.

other

Error
Location
SKILL.md:43
Finding

Unconfirmed Hard-Coded Order Submitted to a Live Brokerage Account

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 43–62
Vulnerability Type: Unsafe live financial transaction execution
Risk Level: Critical

The example explicitly disables sandbox mode and immediately submits a hard-coded order for 10,000 shares without user confirmation, value limits, balance checks, or preview validation:

python
client_config = TigerOpenClientConfig()
client_config.tiger_id = config['tiger_id']
client_config.account = config['account']
client_config.private_key = config['private_key_pk8']
client_config.sandbox = False

client = TradeClient(client_config)

# Place stock order
contracts = client.get_contracts(['02800'])
if contracts:
    order_params = PlaceModifyOrderParams()
    order_params.account = config['account']
    order_params.contract = contracts[0]
    order_params.action = 'BUY'
    order_params.order_type = OrderType.LMT.value
    order_params.limit_price = 26.80  # Get from itiger.com
    order_params.quantity = 10000
    
    result = client.place_order(order_params)
    print(result)

Technical Analysis

Setting client_config.sandbox = False directs the client toward the production trading environment. The subsequent unconditional client.place_order(order_params) call creates a real transaction as soon as the example is executed with valid credentials.

The order parameters are hard-coded rather than derived from an explicitly confirmed user request. There is no dry-run default, transaction preview, maximum notional-value limit, buying-power validation, stale-price check, symbol and market confirmation, or final human approval. At the stated limit price and quantity, the sample represents a notional order value of approximately 268,000 in the instrument's quoted currency, excluding fees and market-specific effects.

The contract lookup only checks whether the returned list is nonempty; it does not independently verify that the select ...[truncated 1720 chars]

Remediation
View remediation

Remediation Suggestions

  • Default all examples to sandbox mode and require a separate, explicit opt-in for production trading.
  • Do not call place_order in a quick-start example. Build and print a redacted order preview instead.
  • Require an explicit confirmation containing the normalized symbol, exchange, side, quantity, order type, limit price, currency, estimated notional value, and account identifier immediately before submission.
  • Require fresh confirmation whenever any order parameter changes; never infer consent from an earlier general request to trade.
  • Replace hard-coded quantities and prices with validated user inputs and reject zero, negative, malformed, or unexpectedly large values.
  • Add configurable per-order and daily notional limits, and fail closed when the proposed order exceeds them.
  • Check buying power, current holdings, market status, lot size, tick size, currency, and exact contract identity before allowing submission.
  • Obtain current market data from an authenticated and appropriately validated source, and reject stale or materially divergent prices.
  • Implement idempotency or duplicate-order protection to prevent retries from creating multiple orders.
  • Clearly label every production operation and log a non-sensitive audit record of the confirmed order.
  • Provide cancellation and incident-response guidance, while noting that cancellation cannot be guaranteed after execution.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (8)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill states it can execute trades but does not prominently warn that the example is configured for real, non-sandbox trading. In the context of a brokerage integration, missing a clear warning materially raises the risk of accidental real-money orders and financial loss.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The description says to use the skill whenever a user wants to buy or sell stocks, manage a portfolio, or check balances, which is broad for a capability that can perform live financial transactions. Overly permissive invocation criteria increase the risk of the skill being selected in ambiguous situations without strong confirmation for high-risk actions.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
92% confidence
Finding

The skill encourages persistent local storage of brokerage credentials in a config file, which creates a lasting secret on disk that may be readable by other processes, backups, or accidental commits. Because the credential grants access to live trading capabilities, session persistence materially increases exposure if the host is compromised or misconfigured.

Content

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

Setup

Create config file at ~/.tiger-config.json :

json
{
  "tiger_id": "your_tiger_id",

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The setup instructs users to store a Tiger account identifier and private key but provides no warning about handling brokerage credentials as sensitive secrets. This omission can lead to insecure storage, sharing, or accidental inclusion in logs or repositories, which is especially dangerous for a skill capable of live trading.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code uses open('~/.tiger-config.json', 'r'), which does not expand ~ and therefore does not reliably read the user's home-directory file as claimed. This can lead users to copy credentials into alternate local paths or modify the code unsafely, and it undermines predictable handling of sensitive brokerage credentials.

Content

No source excerpt is available for this finding.

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
99% confidence
Finding

The example hard-codes client_config.sandbox = False, making live trading the default. In a financial trading skill, unsafe defaults are especially dangerous because a user following the documentation can unintentionally place real-money orders immediately.

Content

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

md
client_config.tiger_id = config['tiger_id']
client_config.account = config['account']
client_config.private_key = config['private_key_pk8']
client_config.sandbox = False

client = TradeClient(client_config)

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The setup documentation tells users to create ~/.tiger-config.json while the example code later opens a different path form, creating a mismatch that can cause users to place credentials in the wrong location or fail open into ad hoc troubleshooting. In a trading skill handling brokerage secrets, confusing config paths increases the chance of insecure workarounds and accidental credential exposure.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The actions section presents labels as 'BUY - 买入' and 'SELL - 卖出', introducing a specific locale/language choice in the skill documentation without stating that multilingual output is optional or user-selectable. This can conflict with a language preference policy when no opt-in or choice is offered.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.