T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/trade.py:28
- Finding
- Live trading password can be transmitted to an arbitrary OpenD host<![CDATA[ ## Vulnerability Details **File Location**: `scripts/trade.py`, lines 28-43 **Vulnerability Type**: Credential exposure through an unrestricted network destination **Risk Level**: High ### Vulnerable Code ```python def open_trade_context(api, args, *, ticker=None): return api.OpenSecTradeContext( host=args.host, port=args.port, filter_trdmarket=get_trade_market(api, ticker=ticker, market_code=args.market), ) def unlock_if_needed(ctx, api, args): if args.env != "real": return False password = env_password(args.unlock_password_env) ret, data = ctx.unlock_trade(password=password) if ret != api.RET_OK: raise RuntimeError(f"unlock_trade failed: {data}") return True ``` The destination is exposed as an unrestricted command-line option: ```python parser.add_argument( "--host", default=DEFAULT_HOST, help=f"OpenD host (default: {DEFAULT_HOST})" ) ``` ### Technical Analysis For real trading, the script reads the trading unlock password from an environment variable and passes it to `ctx.unlock_trade`. The same context is constructed using the user-controlled `--host` and `--port` arguments. The safe default is the loopback address `127.0.0.1`, but the implementation does not enforce that default for password-bearing live-trading operations. It does not require a destination allowlist, explicit remote-host confirmation, authenticated transport, or server identity verification at the skill layer. Consequently, a user or agent that is induced to specify an attacker-controlled host can cause the unlock credential to be submitted to that endpoint. The audit cannot establish from the project code whether the third-party SDK encrypts or authenticates its protocol, so passive plaintext interception is not asserted. Nevertheless, an attacker-controlled endpoint is the active recipient supplied to the SDK and may emulate or proxy the expected OpenD service. Allowing remote OpenD dep ...[truncated 1746 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Enforce loopback destinations for real-trading operations by default: ```python import ipaddress import socket def require_local_live_host(host): addresses = { ipaddress.ip_address(item[4][0]) for item in socket.getaddrinfo(host, None) } if not addresses or not all(address.is_loopback for address in addresses): raise ValueError("Real trading requires a loopback OpenD host.") ``` 2. If remote OpenD access is a required feature, place it behind a separate explicit option such as `--allow-remote-live-opend`, with a prominent warning and an additional interactive confirmation. 3. Require an authenticated and encrypted tunnel, such as mutually authenticated TLS, SSH port forwarding, or a VPN. Prefer connecting the script to a local forwarded port so the password-bearing SDK session still targets loopback. 4. Maintain an allowlist of approved OpenD host identities rather than accepting an arbitrary hostname for live operations. 5. Resolve and validate hostnames carefully to reduce DNS rebinding and multi-address resolution risks. Validate every resolved address and pin the destination during connection establishment. 6. Document that `--confirm` confirms financial mutation but does not establish trust in a remote OpenD endpoint. 7. Keep password values out of logs and exception messages. Continue using an environment variable or, preferably, integrate an operating-system credential store with short-lived retrieval. 8. Add automated tests proving that real trading is rejected for non-loopback hosts unless the dedicated secure-remote override is explicitly enabled. ]]>
