| Version | Supported |
|---|---|
| v3.x (current) | ✅ |
| v2.x and older | ❌ |
If you discover a security vulnerability in Trading Platform v3, please report it responsibly:
- Email: Contact the owner directly (see LICENSE for contact info).
- Do NOT open a public GitHub issue for security vulnerabilities.
- Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
- Acknowledgment: within 48 hours
- Assessment: within 7 days
- Fix (S0/S1): within 14 days
- Fix (S2/S3): within 30 days
- All platform processes bind to
127.0.0.1(localhost only); the only ingress is nginx behind Cloudflare (see Deployment Boundary) - Platform processes run as a dedicated unprivileged unix user (
smc, no sudo) on shared hosts (ADR-0091) - Secrets stored in
.env(gitignored), never committed - Credentials are never logged
- SSOT JSONL writes protected against path traversal
- WebSocket messages have size limits (64 KB)
- User input is sanitized before logging (no log injection)
- Default baseline: all services bind to
127.0.0.1, single-user workstation deployment. - Sanctioned public surface (public site + ADR-0058): Cloudflare → nginx (UFW admits only Cloudflare IP ranges on
:80/:443) → loopbackaiohttpws_server(:8000). Publicly reachable: the chart SPA,/ws,/api/status,/api/context, and five read-onlyGET /api/v3/*endpoints (runtime/api_v3/endpoints.py); every/api/v3/*request requiresX-API-Key. WS connections are capped per server and per IP and actions are token-bucket limited (SEC-06,config.json:ws_server.*).- Token format:
tk_{64 hex}(32 bytes fromsecrets.token_bytes), stored in Redis withSETEXTTL (default 90 days). - Token issuance/revocation tooling:
tools/api_v3/{issue_token,list_tokens,revoke_token}.py. Runbook: docs/runbooks/api_v3_tokens.md. - Agent-adapter endpoints (
/api/archi/*,/api/agent/*, SSE streams) are served only on a private hostname with Bearer auth and are being extracted into a separate off-by-default process (ADR-0090). - Redis
:6379and supervisor stay on loopback; the raw:8000port is never exposed. Shared-host tenant isolation (per-service users, Redis ACL) is tracked in ADR-0091.
- Token format:
- Any further network exposure, multi-user access, or hosted deployment requires a fresh compliance/security review (R_COMPLIANCE).
- Commercial, team, hosted, or redistributed use requires separate written permission from FXCM while ForexConnect remains in the stack.
OWASP API Security Top 10 (2023) coverage for /api/v3/*:
| OWASP | Mitigation | Where |
|---|---|---|
| A01 Broken Access | X-API-Key validated on every request via TokenStore.lookup; unknown/expired token → 401; scope mismatch → 401; fail-closed on Redis error → 503 |
runtime/api_v3/endpoints.py _validate_token |
| A02 Auth Failures | Token entropy = secrets.token_bytes(32).hex() (256-bit, CSPRNG); Redis SETEX TTL (no in-memory cache that survives revocation); revocation = DEL is instant |
runtime/api_v3/token_store.py |
| A03 Property Exposure | Endpoints emit canonical backend shapes verbatim; no ?include_internal flag is honored; sensitive fields opt-in only (currently none) |
endpoints.py |
| A04 Resource Consumption | ?limit hard cap = 100; ?date rejected if older than 90 days; per-IP rate limit 2 r/s burst 30 (= 120 r/min); per-IP limit_conn 20 |
endpoints.py + tools/smc-nginx-v4-hardened.conf |
| A05 Function-level Auth | nginx limit_except GET HEAD OPTIONS { deny all; } zero-trusts non-read methods at the front door; no POST/PUT/DELETE handlers exist in endpoints.py |
nginx vhost |
| A07 ID/Auth Failures | Token store TTL is authoritative; no nginx auth_request cache (avoids stale-after-revoke); Cloudflare "Always HTTPS" enforces TLS browser→edge |
TokenStore + CF |
| A08 Misconfiguration | Empty Access-Control-Allow-Origin "" blocks browser cross-origin reads; X-Content-Type-Options nosniff; Referrer-Policy no-referrer; Cache-Control no-store; structured 404 catch-all (F-S2-004) instead of leaking 500 stack traces |
nginx vhost + endpoints.py _handle_v3_not_found |
| A09 Logging | Structured logs (api_v3_auth_reject token_prefix=…); only first 8 chars of token logged (full token never logged); nginx access logs do not record request headers; rate-limit denials are logged by nginx with consumer IP. Audit JSONL at data_v3/_audit/api_v3_access-YYYY-MM-DD.jsonl (F-S3-002): per-request {ts, consumer, ip_hash, method, path, query, status, latency_ms}. IP hashed with daily-rotating salt (no cross-day correlation, GDPR-friendly); 90-day retention enforced on registration. Token values never persisted. |
endpoints.py audit_middleware + nginx |
| A10 SSRF | Not applicable — endpoints accept no URL/host parameters; all data sources are local filesystem or in-process state | n/a |
Every /api/v3/* JSON envelope includes a top-level disclaimer field:
"Educational/research data only. Not financial advice. No recommendation to buy or sell any instrument."
Consumers MUST surface this disclaimer (or an equivalent) to end users. Stripping it violates the API contract.
- CI workflow:
.github/workflows/ci.yml - Static governance gates:
python -m tools.run_exit_gates --manifest tools/exit_gates/manifest.ci.json - Python dependency scan:
pip-audit -r requirements.txt - Python SAST baseline:
bandit -q -r app core runtime tools - Frontend dependency scan:
npm audit --audit-level=high --omit=dev - Dependency drift automation:
.github/dependabot.yml
- Vulnerabilities in third-party dependencies (report to upstream)
- Issues requiring physical access to the host machine
- Social engineering attacks