# Security Profile ## Audit Status **Grade: A+ — 30/30 checks — Last audit: 2026-05-31** ## Authentication Stack | Layer | Implementation | |---|---| | Transport | Bearer token in `Authorization` header | | Token format | HMAC-SHA256 JWT, 1-hour expiry | | Signing secret | 64-char hex (256-bit), stored in `wp_options` as `asktam_mcp_jwt_secret` | | JWT claims | `key_id`, `iss` (site URL), `exp`, `jti` (revocable) | | Comparison | `hash_equals()` — timing-safe | | Key storage | bcrypt hash in `mwp_asktam_mcp_keys.key_hash` | | Revocation | JTI revocation list in `wp_options` | | Secret rotation | `TokenManager::rotateSecret()` — invalidates all existing tokens | ## Scope Enforcement Every tool call validates the required scope against the key's granted scopes: ``` read → read-only operations write → create / update / delete admin → plugin mgmt, CLI, raw SQL, options elementor → Elementor builder tools woo → WooCommerce tools ``` ## Rate Limiting - Default: 60 requests/minute per API key - Stored via WP transients (`asktam_mcp_rate_{key_id}`) - Configurable in WP Admin → AskTam MCP → Settings - Returns HTTP 429 on exceed ## HTTPS Enforcement When enabled (default: on), requests over HTTP are rejected with 403. Configurable in Settings tab. ## Input Sanitization | Attack vector | Protection | |---|---| | SQL injection | `wpdb->prepare()` everywhere, `(int)` casts on IDs | | XSS | 47 `esc_*()` calls in AdminMenu | | CSRF | `wp_verify_nonce()` on all 3 admin form actions | | Path traversal | `realpath()` + uploads/ sandbox in FileManager | | Shell injection | Allowlisted commands + `escapeshellarg()` in CLI | | Raw SQL abuse | SELECT-only enforcement + disabled by default | ## Audit Logging Every tool call is logged to `mwp_asktam_mcp_log`: - `key_id` (not raw token) - `tool` name - `ip` address - `status` (success/error) - `duration_ms` - `created_at` Viewable in WP Admin → AskTam MCP → Audit Log (last 1000 entries). ## Raw SQL Disabled by default. When enabled, only `SELECT` statements are allowed. All DML (`INSERT`, `UPDATE`, `DELETE`, `DROP`, `TRUNCATE`, etc.) is blocked via pattern matching. ## File Manager Sandbox Restricted to `wp_upload_dir()['basedir']` via `realpath()` verification. Any attempt to traverse outside uploads/ returns an error. ## CLI Allowlist Only explicitly allowed WP-CLI commands are executable. All arguments are passed through `escapeshellarg()`. The `--allow-root` flag is always appended (required since container runs as root). ## 30-Check Audit Points 1. Bearer token check present in request flow 2. Bearer token extraction from Authorization header 3. HTTPS enforcement code path in Auth::validate() 4. HTTPS enforcement enabled in settings 5. Rate limiting via transients 6. Scope enforcement per request 7. `hash_equals()` for timing-safe HMAC comparison 8. HMAC-SHA256 algorithm used 9. JWT `exp` claim validated 10. JWT `iss` claim validated against site URL 11. JTI revocation list implemented 12. JWT secret rotation supported 13. JWT secret is 64+ chars 14. CSRF: `wp_verify_nonce` on admin form actions (3+ checks) 15. XSS: 20+ `esc_*()` calls in AdminMenu 16. `wpdb->prepare()` in TokenManager queries 17. `(int)` casts on post_id in PostTools 18. SELECT-only enforcement in RawSqlTools 19. Raw SQL disabled by default 20. FileManager sandboxed to uploads dir 21. CLI allowlist present 22. CLI injection blocking present 23. `escapeshellarg()` in CLI 24. `--allow-root` flag in CLI (container runs as root) 25. Rate limit configured (> 0, ≤ 600) 26. Request logging enabled 27. Audit log table exists 28. Log table stores key_id only (not raw tokens) 29. At least one active API key exists 30. Admin-scoped keys are labeled